sima-cli 2.1.5__tar.gz → 2.1.7__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.
- {sima_cli-2.1.5/sima_cli.egg-info → sima_cli-2.1.7}/PKG-INFO +34 -2
- {sima_cli-2.1.5 → sima_cli-2.1.7}/README.md +33 -1
- {sima_cli-2.1.5 → sima_cli-2.1.7}/pyproject.toml +1 -1
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/__version__.py +1 -1
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/cli.py +52 -5
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/metadata_installer.py +4 -0
- sima_cli-2.1.7/sima_cli/install/package_builder.py +304 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/registry.py +49 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/commands.py +15 -2
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/install.py +201 -7
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/neat.py +65 -16
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/preinstall.py +10 -3
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/utils.py +32 -6
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/elxr.py +116 -10
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/netboot.py +126 -24
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/updater.py +11 -16
- sima_cli-2.1.7/sima_cli/vulcan/__init__.py +3 -0
- sima_cli-2.1.7/sima_cli/vulcan/artifacts.py +452 -0
- sima_cli-2.1.7/sima_cli/vulcan/commands.py +226 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7/sima_cli.egg-info}/PKG-INFO +34 -2
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli.egg-info/SOURCES.txt +9 -1
- sima_cli-2.1.7/tests/unit/test_elxr_update.py +408 -0
- sima_cli-2.1.7/tests/unit/test_firmware.py +60 -0
- sima_cli-2.1.7/tests/unit/test_metadata_installer.py +21 -0
- sima_cli-2.1.7/tests/unit/test_netboot.py +98 -0
- sima_cli-2.1.7/tests/unit/test_package_builder.py +314 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_sdk_image_detection.py +377 -5
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_sdk_preinstall.py +14 -0
- sima_cli-2.1.7/tests/unit/test_vulcan.py +445 -0
- sima_cli-2.1.5/tests/unit/test_elxr_update.py +0 -110
- sima_cli-2.1.5/tests/unit/test_firmware.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/LICENSE +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/MANIFEST.in +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/requirements.txt +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/setup.cfg +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/setup.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/__main__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/app_zoo/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/app_zoo/app.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/app_zoo/commands.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/auth/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/auth/auth0.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/auth/devportal.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/auth/login.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/auth/oauth.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/data/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/data/resources_internal.yaml +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/data/resources_public.yaml +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/deploy_only/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/deploy_only/device/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/deploy_only/device/commands.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/deploy_only/mpk/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/deploy_only/mpk/commands.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/discover/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/discover/discover.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/discover/linuxll.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/download/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/download/downloader.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/github_assets.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/hostdriver.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/metadata_info.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/metadata_validator.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/optiview.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/install/palette.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/mla/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/mla/meminfo.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/model_zoo/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/model_zoo/model.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/network/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/network/network.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/playbooks/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/playbooks/commands.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/playbooks/manager.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/cmdexec.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/config.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/linux_shared_network.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/requirements.json +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/script.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/stop.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/sdk/uninstall.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/serial/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/serial/serial.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/storage/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/storage/nvme.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/storage/sdcard.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/bmaptool.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/bootimg.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/cleanlog.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/local.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/query.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/update/remote.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/upgrade/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/upgrade/selfupdate.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/api_common.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/artifactory.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/common.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/config.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/config_loader.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/container_registries.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/device_api.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/disk.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/docker.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/env.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/errors.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/mpk_api.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/net.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/network.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/pcie.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/pkg_update_check.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/serializers.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/services.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli/utils/tag.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli.egg-info/dependency_links.txt +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli.egg-info/entry_points.txt +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli.egg-info/requires.txt +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/sima_cli.egg-info/top_level.txt +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/e2e/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/__init__.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_app_zoo.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_auth.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_cli.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_cli_stdio.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_download.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_install_stub.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_model_zoo.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_pkg_update_check.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_sdk_uninstall.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_selfupdate.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_skills_commands.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_skills_manager.py +0 -0
- {sima_cli-2.1.5 → sima_cli-2.1.7}/tests/unit/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sima-cli
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.7
|
|
4
4
|
Summary: CLI tool for SiMa Developer Portal to download models, firmware, and apps.
|
|
5
5
|
Home-page: https://developer.sima.ai/
|
|
6
6
|
Author: SiMa.ai
|
|
@@ -152,6 +152,34 @@ sima-cli download <URL> [-d DEST]
|
|
|
152
152
|
|
|
153
153
|
---
|
|
154
154
|
|
|
155
|
+
## 🔥 Vulcan Artifacts
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
sima-cli vulcan download --env production core main
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- Downloads tested artifacts from Vulcan artifact hosting.
|
|
162
|
+
- Environment URLs:
|
|
163
|
+
- `dev`: `https://artifacts.neat.paconsultings.com`
|
|
164
|
+
- `staging`: `https://artifacts.stg.neat.sima.ai`
|
|
165
|
+
- `production`: `https://artifacts.neat.sima.ai`
|
|
166
|
+
- `staging` and `production` are not yet available for Vulcan downloads; use `--env dev` for now.
|
|
167
|
+
- Usage:
|
|
168
|
+
- `sima-cli vulcan --env {dev|staging|production} download [REPO] [BRANCH_OR_TAG]`
|
|
169
|
+
- `sima-cli vulcan download --env {dev|staging|production} [REPO] [BRANCH_OR_TAG]`
|
|
170
|
+
- If `REPO` is omitted, the CLI prompts for a repository.
|
|
171
|
+
- If `BRANCH_OR_TAG` is omitted, the CLI downloads `branches.json` and prompts for a branch.
|
|
172
|
+
- For automation, pass both values and add `--json` for structured output.
|
|
173
|
+
- Each download reads `latest.tag`, fetches `manifest.json`, downloads manifest-listed artifacts, and verifies size and SHA256 values when present.
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
sima-cli vulcan --env dev download internals develop --output ./artifacts --json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
155
183
|
## 🔧 Firmware Update
|
|
156
184
|
|
|
157
185
|
```bash
|
|
@@ -379,10 +407,14 @@ sima-cli packages show <PACKAGE_NAME> [--version VERSION]
|
|
|
379
407
|
### Setup SDK
|
|
380
408
|
|
|
381
409
|
```bash
|
|
382
|
-
sima-cli sdk setup [--noninteractive] [-y]
|
|
410
|
+
sima-cli sdk setup [--noninteractive] [-y] [--workspace PATH] [--no-model-sdk] [--no-insight] [--minimal]
|
|
383
411
|
```
|
|
384
412
|
|
|
385
413
|
- Initialize SDK environment and select components to start.
|
|
414
|
+
- Use `--workspace PATH` to mount a host directory other than `~/workspace` into SDK containers.
|
|
415
|
+
- Use `--no-model-sdk` to skip Model SDK extension setup.
|
|
416
|
+
- Use `--no-insight` to start Neat SDK without Insight UI/video/WebRTC port mappings.
|
|
417
|
+
- Use `--minimal` for CI compilation jobs. It skips optional Neat SDK setup extras, including Insight setup, installing `sima-cli`, Model SDK extensions, and coding agent playbooks inside the container.
|
|
386
418
|
|
|
387
419
|
### Start SDK Containers
|
|
388
420
|
|
|
@@ -118,6 +118,34 @@ sima-cli download <URL> [-d DEST]
|
|
|
118
118
|
|
|
119
119
|
---
|
|
120
120
|
|
|
121
|
+
## 🔥 Vulcan Artifacts
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
sima-cli vulcan download --env production core main
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- Downloads tested artifacts from Vulcan artifact hosting.
|
|
128
|
+
- Environment URLs:
|
|
129
|
+
- `dev`: `https://artifacts.neat.paconsultings.com`
|
|
130
|
+
- `staging`: `https://artifacts.stg.neat.sima.ai`
|
|
131
|
+
- `production`: `https://artifacts.neat.sima.ai`
|
|
132
|
+
- `staging` and `production` are not yet available for Vulcan downloads; use `--env dev` for now.
|
|
133
|
+
- Usage:
|
|
134
|
+
- `sima-cli vulcan --env {dev|staging|production} download [REPO] [BRANCH_OR_TAG]`
|
|
135
|
+
- `sima-cli vulcan download --env {dev|staging|production} [REPO] [BRANCH_OR_TAG]`
|
|
136
|
+
- If `REPO` is omitted, the CLI prompts for a repository.
|
|
137
|
+
- If `BRANCH_OR_TAG` is omitted, the CLI downloads `branches.json` and prompts for a branch.
|
|
138
|
+
- For automation, pass both values and add `--json` for structured output.
|
|
139
|
+
- Each download reads `latest.tag`, fetches `manifest.json`, downloads manifest-listed artifacts, and verifies size and SHA256 values when present.
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
sima-cli vulcan --env dev download internals develop --output ./artifacts --json
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
121
149
|
## 🔧 Firmware Update
|
|
122
150
|
|
|
123
151
|
```bash
|
|
@@ -345,10 +373,14 @@ sima-cli packages show <PACKAGE_NAME> [--version VERSION]
|
|
|
345
373
|
### Setup SDK
|
|
346
374
|
|
|
347
375
|
```bash
|
|
348
|
-
sima-cli sdk setup [--noninteractive] [-y]
|
|
376
|
+
sima-cli sdk setup [--noninteractive] [-y] [--workspace PATH] [--no-model-sdk] [--no-insight] [--minimal]
|
|
349
377
|
```
|
|
350
378
|
|
|
351
379
|
- Initialize SDK environment and select components to start.
|
|
380
|
+
- Use `--workspace PATH` to mount a host directory other than `~/workspace` into SDK containers.
|
|
381
|
+
- Use `--no-model-sdk` to skip Model SDK extension setup.
|
|
382
|
+
- Use `--no-insight` to start Neat SDK without Insight UI/video/WebRTC port mappings.
|
|
383
|
+
- Use `--minimal` for CI compilation jobs. It skips optional Neat SDK setup extras, including Insight setup, installing `sima-cli`, Model SDK extensions, and coding agent playbooks inside the container.
|
|
352
384
|
|
|
353
385
|
### Start SDK Containers
|
|
354
386
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# sima_cli/__version__.py
|
|
2
|
-
__version__ = "2.1.
|
|
2
|
+
__version__ = "2.1.7"
|
|
@@ -29,6 +29,8 @@ from sima_cli.app_zoo.commands import register_appzoo_commands
|
|
|
29
29
|
from sima_cli.install.registry import register_packages_commands
|
|
30
30
|
from sima_cli.upgrade.selfupdate import register_selfupdate_command
|
|
31
31
|
from sima_cli.playbooks import register_playbook_commands
|
|
32
|
+
from sima_cli.vulcan import register_vulcan_commands
|
|
33
|
+
from sima_cli.vulcan.commands import ENV_BASE_URLS, install_vulcan_package
|
|
32
34
|
|
|
33
35
|
def _configure_stdio_errors() -> None:
|
|
34
36
|
for stream in (getattr(sys, "stdout", None), getattr(sys, "stderr", None)):
|
|
@@ -89,6 +91,7 @@ def main(ctx, internal):
|
|
|
89
91
|
# ----------------------
|
|
90
92
|
register_sdk_commands(main)
|
|
91
93
|
register_playbook_commands(main)
|
|
94
|
+
register_vulcan_commands(main)
|
|
92
95
|
|
|
93
96
|
|
|
94
97
|
# ----------------------
|
|
@@ -502,7 +505,31 @@ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
|
|
502
505
|
@click.argument("component", required=False)
|
|
503
506
|
@click.option("-v", "--version", help="SDK version (required for SDK-dependent components unless --metadata is provided)")
|
|
504
507
|
@click.option("-m", "--mirror", help="URL to a metadata.json file for generic installation")
|
|
505
|
-
@click.option("-t", "--tag", help="Tag of the package
|
|
508
|
+
@click.option("-t", "--tag", help="Tag of the package. With --vulcan, metadata variant type such as minimum.")
|
|
509
|
+
@click.option("--vulcan", "use_vulcan", is_flag=True, help="Install from Vulcan artifacts using the Vulcan package resolver.")
|
|
510
|
+
@click.option(
|
|
511
|
+
"--env",
|
|
512
|
+
"vulcan_environment",
|
|
513
|
+
type=click.Choice(sorted(ENV_BASE_URLS), case_sensitive=False),
|
|
514
|
+
default=None,
|
|
515
|
+
help="Vulcan artifact environment. Used with --vulcan. Defaults to production.",
|
|
516
|
+
)
|
|
517
|
+
@click.option(
|
|
518
|
+
"--base-url",
|
|
519
|
+
"vulcan_base_url",
|
|
520
|
+
default=None,
|
|
521
|
+
envvar="SIMA_VULCAN_BASE_URL",
|
|
522
|
+
help="Override the Vulcan artifact base URL. Used with --vulcan.",
|
|
523
|
+
)
|
|
524
|
+
@click.option(
|
|
525
|
+
"-d",
|
|
526
|
+
"--install-dir",
|
|
527
|
+
default=".",
|
|
528
|
+
show_default=True,
|
|
529
|
+
type=click.Path(file_okay=False, dir_okay=True, path_type=str),
|
|
530
|
+
help="Directory where Vulcan package resources are downloaded and installed. Used with --vulcan.",
|
|
531
|
+
)
|
|
532
|
+
@click.option("--json", "json_output", is_flag=True, help="With --vulcan, print resolved metadata URL and exit.")
|
|
506
533
|
@click.option(
|
|
507
534
|
"-f",
|
|
508
535
|
"--force",
|
|
@@ -511,7 +538,7 @@ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
|
|
511
538
|
help="Force installation even if compatibility checks fail.",
|
|
512
539
|
)
|
|
513
540
|
@click.pass_context
|
|
514
|
-
def install_cmd(ctx, component, version, mirror, tag, force):
|
|
541
|
+
def install_cmd(ctx, component, version, mirror, tag, use_vulcan, vulcan_environment, vulcan_base_url, install_dir, json_output, force):
|
|
515
542
|
"""
|
|
516
543
|
Install SiMa packages.
|
|
517
544
|
|
|
@@ -546,12 +573,29 @@ def install_cmd(ctx, component, version, mirror, tag, force):
|
|
|
546
573
|
"""
|
|
547
574
|
internal = ctx.obj.get("internal", False)
|
|
548
575
|
|
|
576
|
+
if use_vulcan:
|
|
577
|
+
if mirror:
|
|
578
|
+
raise click.ClickException("--mirror cannot be used with --vulcan.")
|
|
579
|
+
if not component:
|
|
580
|
+
raise click.ClickException("You must specify a Vulcan target when using --vulcan.")
|
|
581
|
+
install_vulcan_package(
|
|
582
|
+
target=component,
|
|
583
|
+
environment=vulcan_environment or "production",
|
|
584
|
+
base_url=vulcan_base_url,
|
|
585
|
+
package_type=tag,
|
|
586
|
+
install_dir=install_dir,
|
|
587
|
+
force=force,
|
|
588
|
+
json_output=json_output,
|
|
589
|
+
)
|
|
590
|
+
return None
|
|
591
|
+
|
|
549
592
|
# Metadata-based installation path
|
|
550
593
|
if mirror:
|
|
551
594
|
if component:
|
|
552
595
|
click.echo(f"⚠️ Component '{component}' is ignored when using --metadata. Proceeding with metadata-based installation.")
|
|
553
596
|
click.echo(f"🔧 Installing generic component from metadata URL: {mirror}")
|
|
554
|
-
|
|
597
|
+
install_from_metadata(metadata_url=mirror, internal=internal, force=force)
|
|
598
|
+
return None
|
|
555
599
|
|
|
556
600
|
# No component and no metadata: error
|
|
557
601
|
if not component:
|
|
@@ -560,11 +604,13 @@ def install_cmd(ctx, component, version, mirror, tag, force):
|
|
|
560
604
|
|
|
561
605
|
# if user specified gh: as component, treat it the same as -m
|
|
562
606
|
if component.startswith("gh:"):
|
|
563
|
-
|
|
607
|
+
install_from_metadata(metadata_url=component, internal=False, force=force)
|
|
608
|
+
return None
|
|
564
609
|
|
|
565
610
|
# if the user specified cr: or ghcr: as component, install from container registry
|
|
566
611
|
if component.startswith("cr:") or component.startswith("ghcr:"):
|
|
567
|
-
|
|
612
|
+
install_from_cr(resource_spec=component, internal=internal)
|
|
613
|
+
return None
|
|
568
614
|
|
|
569
615
|
version = resolve_version(version)
|
|
570
616
|
|
|
@@ -599,6 +645,7 @@ def install_cmd(ctx, component, version, mirror, tag, force):
|
|
|
599
645
|
ctx.exit(1)
|
|
600
646
|
|
|
601
647
|
click.echo("✅ Installation complete.")
|
|
648
|
+
return None
|
|
602
649
|
|
|
603
650
|
|
|
604
651
|
# ----------------------
|
|
@@ -936,6 +936,10 @@ def _is_platform_compatible(metadata: dict, force: bool = False) -> bool:
|
|
|
936
936
|
platforms = metadata.get("platforms", [])
|
|
937
937
|
board_ver, _ = get_sima_build_version()
|
|
938
938
|
|
|
939
|
+
if not platforms:
|
|
940
|
+
click.echo("ℹ️ No platform restrictions specified; treating package as compatible with all platforms.")
|
|
941
|
+
return True
|
|
942
|
+
|
|
939
943
|
# Detect current OS and version
|
|
940
944
|
os_name = platform.system().lower() # 'darwin', 'windows', 'linux'
|
|
941
945
|
os_version = "Unknown"
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Optional, Sequence, Tuple
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
METADATA_FILENAME = "metadata.json"
|
|
14
|
+
DEFAULT_POST_MESSAGE = "[bold]Package installed successfully.[/bold]\n"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _run_git(args: List[str], cwd: Path) -> Optional[str]:
|
|
18
|
+
try:
|
|
19
|
+
result = subprocess.run(
|
|
20
|
+
["git"] + args,
|
|
21
|
+
cwd=str(cwd),
|
|
22
|
+
check=True,
|
|
23
|
+
stdout=subprocess.PIPE,
|
|
24
|
+
stderr=subprocess.DEVNULL,
|
|
25
|
+
text=True,
|
|
26
|
+
)
|
|
27
|
+
except (OSError, subprocess.CalledProcessError):
|
|
28
|
+
return None
|
|
29
|
+
return result.stdout.strip()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _git_root(search_dir: Path) -> Optional[Path]:
|
|
33
|
+
output = _run_git(["rev-parse", "--show-toplevel"], search_dir)
|
|
34
|
+
if not output:
|
|
35
|
+
return None
|
|
36
|
+
return Path(output).resolve()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _github_repo_from_remote(remote_url: str) -> Optional[Tuple[str, str]]:
|
|
40
|
+
value = remote_url.strip()
|
|
41
|
+
if value.endswith(".git"):
|
|
42
|
+
value = value[:-4]
|
|
43
|
+
|
|
44
|
+
if value.startswith("git@github.com:"):
|
|
45
|
+
value = value[len("git@github.com:"):]
|
|
46
|
+
elif value.startswith("https://github.com/"):
|
|
47
|
+
value = value[len("https://github.com/"):]
|
|
48
|
+
elif value.startswith("http://github.com/"):
|
|
49
|
+
value = value[len("http://github.com/"):]
|
|
50
|
+
else:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
parts = value.split("/")
|
|
54
|
+
if len(parts) != 2 or not parts[0] or not parts[1]:
|
|
55
|
+
return None
|
|
56
|
+
return parts[0], parts[1]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def resolve_git_context(artifacts_folder: Path) -> Tuple[Optional[Path], Optional[Tuple[str, str]]]:
|
|
60
|
+
for search_dir in (Path.cwd(), artifacts_folder):
|
|
61
|
+
root = _git_root(search_dir)
|
|
62
|
+
if not root:
|
|
63
|
+
continue
|
|
64
|
+
remote = _run_git(["remote", "get-url", "origin"], root) or ""
|
|
65
|
+
return root, _github_repo_from_remote(remote)
|
|
66
|
+
return None, None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def default_package_name(artifacts_folder: Path) -> str:
|
|
70
|
+
git_root, github_repo = resolve_git_context(artifacts_folder)
|
|
71
|
+
if github_repo:
|
|
72
|
+
owner, repo = github_repo
|
|
73
|
+
return "gh:{}/{}".format(owner, repo)
|
|
74
|
+
if git_root:
|
|
75
|
+
return git_root.name
|
|
76
|
+
return Path.cwd().name
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def default_version(artifacts_folder: Path) -> str:
|
|
80
|
+
git_root, _github_repo = resolve_git_context(artifacts_folder)
|
|
81
|
+
if git_root:
|
|
82
|
+
tag = _run_git(["describe", "--tags", "--exact-match"], git_root)
|
|
83
|
+
if tag:
|
|
84
|
+
return tag
|
|
85
|
+
commit = _run_git(["rev-parse", "--short", "HEAD"], git_root)
|
|
86
|
+
if commit:
|
|
87
|
+
return commit
|
|
88
|
+
return datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def github_repo_description(artifacts_folder: Path) -> str:
|
|
92
|
+
_git_root, github_repo = resolve_git_context(artifacts_folder)
|
|
93
|
+
if not github_repo:
|
|
94
|
+
return ""
|
|
95
|
+
|
|
96
|
+
owner, repo = github_repo
|
|
97
|
+
headers = {"Accept": "application/vnd.github+json"}
|
|
98
|
+
token = os.getenv("GITHUB_TOKEN")
|
|
99
|
+
if token:
|
|
100
|
+
headers["Authorization"] = "Bearer {}".format(token)
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
response = requests.get(
|
|
104
|
+
"https://api.github.com/repos/{}/{}".format(owner, repo),
|
|
105
|
+
headers=headers,
|
|
106
|
+
timeout=5,
|
|
107
|
+
)
|
|
108
|
+
response.raise_for_status()
|
|
109
|
+
description = response.json().get("description")
|
|
110
|
+
except Exception:
|
|
111
|
+
return ""
|
|
112
|
+
return description if isinstance(description, str) else ""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _sha256_file(path: Path) -> str:
|
|
116
|
+
hasher = hashlib.sha256()
|
|
117
|
+
with path.open("rb") as file_obj:
|
|
118
|
+
for chunk in iter(lambda: file_obj.read(1024 * 1024), b""):
|
|
119
|
+
hasher.update(chunk)
|
|
120
|
+
return hasher.hexdigest()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _format_size(total_bytes: int) -> str:
|
|
124
|
+
units = (("GB", 1024 ** 3), ("MB", 1024 ** 2), ("KB", 1024))
|
|
125
|
+
if total_bytes <= 0:
|
|
126
|
+
return "0KB"
|
|
127
|
+
for suffix, factor in units:
|
|
128
|
+
if total_bytes >= factor:
|
|
129
|
+
value = total_bytes / factor
|
|
130
|
+
text = "{:.1f}".format(value).rstrip("0").rstrip(".")
|
|
131
|
+
return "{}{}".format(text, suffix)
|
|
132
|
+
return "1KB"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _matches_exclude_pattern(resource: str, patterns: Sequence[str]) -> bool:
|
|
136
|
+
filename = Path(resource).name
|
|
137
|
+
return any(pattern and (pattern in resource or pattern in filename) for pattern in patterns)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def collect_artifact_resources(
|
|
141
|
+
artifacts_folder: Path,
|
|
142
|
+
exclude_patterns: Optional[Sequence[str]] = None,
|
|
143
|
+
) -> Tuple[List[str], Dict[str, Path], int]:
|
|
144
|
+
resources = []
|
|
145
|
+
resource_paths = {}
|
|
146
|
+
total_size = 0
|
|
147
|
+
normalized_excludes = [pattern.strip() for pattern in (exclude_patterns or []) if pattern.strip()]
|
|
148
|
+
|
|
149
|
+
for path in sorted(artifacts_folder.rglob("*")):
|
|
150
|
+
if not path.is_file():
|
|
151
|
+
continue
|
|
152
|
+
rel = path.relative_to(artifacts_folder).as_posix()
|
|
153
|
+
if rel == METADATA_FILENAME or re.fullmatch(r"metadata-[A-Za-z0-9_.-]+\.json", rel):
|
|
154
|
+
continue
|
|
155
|
+
if _matches_exclude_pattern(rel, normalized_excludes):
|
|
156
|
+
continue
|
|
157
|
+
resources.append(rel)
|
|
158
|
+
resource_paths[rel] = path
|
|
159
|
+
total_size += path.stat().st_size
|
|
160
|
+
|
|
161
|
+
return resources, resource_paths, total_size
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def parse_selectables(selectables: Optional[str]) -> List[Tuple[str, str]]:
|
|
165
|
+
if not selectables:
|
|
166
|
+
return []
|
|
167
|
+
|
|
168
|
+
parsed = []
|
|
169
|
+
for raw_item in selectables.split(";"):
|
|
170
|
+
item = raw_item.strip()
|
|
171
|
+
if not item:
|
|
172
|
+
continue
|
|
173
|
+
if ":" not in item:
|
|
174
|
+
raise ValueError("selectables entries must use 'name:file' format")
|
|
175
|
+
name, resource = item.split(":", 1)
|
|
176
|
+
name = name.strip()
|
|
177
|
+
resource = resource.strip()
|
|
178
|
+
if not name or not resource:
|
|
179
|
+
raise ValueError("selectables entries must include both name and file")
|
|
180
|
+
parsed.append((name, resource))
|
|
181
|
+
return parsed
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def build_selectable_resources(
|
|
185
|
+
selectable_specs: List[Tuple[str, str]],
|
|
186
|
+
resource_paths: Dict[str, Path],
|
|
187
|
+
) -> List[Dict[str, str]]:
|
|
188
|
+
selectable_resources = []
|
|
189
|
+
seen_resources = set()
|
|
190
|
+
|
|
191
|
+
for name, resource in selectable_specs:
|
|
192
|
+
if resource not in resource_paths:
|
|
193
|
+
raise ValueError("selectable resource is not in artifacts-folder: {}".format(resource))
|
|
194
|
+
if resource in seen_resources:
|
|
195
|
+
raise ValueError("duplicate selectable resource: {}".format(resource))
|
|
196
|
+
seen_resources.add(resource)
|
|
197
|
+
selectable_resources.append({
|
|
198
|
+
"name": name,
|
|
199
|
+
"url": "",
|
|
200
|
+
"resource": resource,
|
|
201
|
+
"checksum": _sha256_file(resource_paths[resource]),
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return selectable_resources
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def resolve_install_script(artifacts_folder: Path, install_script: str) -> str:
|
|
208
|
+
script = install_script.strip()
|
|
209
|
+
if not script:
|
|
210
|
+
raise ValueError("install-script cannot be empty")
|
|
211
|
+
|
|
212
|
+
candidate_paths = []
|
|
213
|
+
raw_path = Path(script).expanduser()
|
|
214
|
+
if raw_path.is_absolute():
|
|
215
|
+
candidate_paths.append(raw_path)
|
|
216
|
+
else:
|
|
217
|
+
candidate_paths.append(artifacts_folder / raw_path)
|
|
218
|
+
candidate_paths.append(Path.cwd() / raw_path)
|
|
219
|
+
|
|
220
|
+
artifacts_root = artifacts_folder.resolve()
|
|
221
|
+
for candidate in candidate_paths:
|
|
222
|
+
try:
|
|
223
|
+
resolved = candidate.resolve()
|
|
224
|
+
except OSError:
|
|
225
|
+
continue
|
|
226
|
+
if not resolved.is_file():
|
|
227
|
+
continue
|
|
228
|
+
try:
|
|
229
|
+
rel = resolved.relative_to(artifacts_root).as_posix()
|
|
230
|
+
except ValueError:
|
|
231
|
+
continue
|
|
232
|
+
return "./{}".format(rel)
|
|
233
|
+
|
|
234
|
+
return script
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def build_metadata(
|
|
238
|
+
artifacts_folder: Path,
|
|
239
|
+
name: Optional[str] = None,
|
|
240
|
+
version: Optional[str] = None,
|
|
241
|
+
description: Optional[str] = None,
|
|
242
|
+
install_script: str = "",
|
|
243
|
+
selectables: Optional[str] = None,
|
|
244
|
+
exclude: Optional[Sequence[str]] = None,
|
|
245
|
+
) -> Dict:
|
|
246
|
+
artifacts_folder = artifacts_folder.expanduser().resolve()
|
|
247
|
+
if not artifacts_folder.is_dir():
|
|
248
|
+
raise ValueError("artifacts-folder is not a directory: {}".format(artifacts_folder))
|
|
249
|
+
|
|
250
|
+
resources, resource_paths, total_size = collect_artifact_resources(artifacts_folder, exclude_patterns=exclude)
|
|
251
|
+
if not resources:
|
|
252
|
+
raise ValueError("artifacts-folder does not contain any artifact files")
|
|
253
|
+
|
|
254
|
+
selectable_specs = parse_selectables(selectables)
|
|
255
|
+
selectable_resources = build_selectable_resources(selectable_specs, resource_paths)
|
|
256
|
+
selectable_resource_names = {entry["resource"] for entry in selectable_resources}
|
|
257
|
+
resources = [resource for resource in resources if resource not in selectable_resource_names]
|
|
258
|
+
checksums = {
|
|
259
|
+
resource: _sha256_file(path)
|
|
260
|
+
for resource, path in resource_paths.items()
|
|
261
|
+
if resource not in selectable_resource_names
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
resolved_description = description
|
|
265
|
+
if resolved_description is None:
|
|
266
|
+
resolved_description = github_repo_description(artifacts_folder)
|
|
267
|
+
|
|
268
|
+
size_text = _format_size(total_size)
|
|
269
|
+
return {
|
|
270
|
+
"name": name or default_package_name(artifacts_folder),
|
|
271
|
+
"version": version or default_version(artifacts_folder),
|
|
272
|
+
"release": "",
|
|
273
|
+
"description": resolved_description or "",
|
|
274
|
+
"platforms": [],
|
|
275
|
+
"resources": resources,
|
|
276
|
+
"resources-checksum": checksums,
|
|
277
|
+
"selectable-resources": selectable_resources,
|
|
278
|
+
"size": {
|
|
279
|
+
"download": size_text,
|
|
280
|
+
"install": size_text,
|
|
281
|
+
},
|
|
282
|
+
"installation": {
|
|
283
|
+
"script": resolve_install_script(artifacts_folder, install_script),
|
|
284
|
+
"post-message": DEFAULT_POST_MESSAGE,
|
|
285
|
+
},
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def metadata_filename(variant: Optional[str] = None) -> str:
|
|
290
|
+
if variant is None:
|
|
291
|
+
return METADATA_FILENAME
|
|
292
|
+
|
|
293
|
+
normalized = variant.strip()
|
|
294
|
+
if not normalized:
|
|
295
|
+
return METADATA_FILENAME
|
|
296
|
+
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*", normalized):
|
|
297
|
+
raise ValueError("variant must contain only letters, numbers, dots, underscores, or hyphens")
|
|
298
|
+
return "metadata-{}.json".format(normalized)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def write_metadata(artifacts_folder: Path, metadata: Dict, variant: Optional[str] = None) -> Path:
|
|
302
|
+
output_path = artifacts_folder.expanduser().resolve() / metadata_filename(variant)
|
|
303
|
+
output_path.write_text(json.dumps(metadata, indent=4) + "\n", encoding="utf-8")
|
|
304
|
+
return output_path
|
|
@@ -19,6 +19,7 @@ import json
|
|
|
19
19
|
import click
|
|
20
20
|
import datetime
|
|
21
21
|
from pathlib import Path
|
|
22
|
+
from sima_cli.install.package_builder import build_metadata, write_metadata
|
|
22
23
|
from rich.table import Table
|
|
23
24
|
from rich.console import Console
|
|
24
25
|
from rich.syntax import Syntax
|
|
@@ -74,6 +75,54 @@ def show_metadata(name, version):
|
|
|
74
75
|
except ValueError as e:
|
|
75
76
|
Console().print(f"[red]Error:[/red] {e}")
|
|
76
77
|
|
|
78
|
+
|
|
79
|
+
@packages.command("build", help="Build package metadata.json from an artifacts folder.")
|
|
80
|
+
@click.argument(
|
|
81
|
+
"artifacts_folder",
|
|
82
|
+
metavar="ARTIFACTS_FOLDER",
|
|
83
|
+
type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
|
|
84
|
+
)
|
|
85
|
+
@click.option("--name", help="Package name. Defaults to gh:<org>/<repo> for GitHub repos.")
|
|
86
|
+
@click.option("--version", help="Package version. Defaults to exact git tag or short commit hash.")
|
|
87
|
+
@click.option("--description", help="Package description. Defaults to the GitHub repo description when available.")
|
|
88
|
+
@click.option(
|
|
89
|
+
"--install-script",
|
|
90
|
+
required=True,
|
|
91
|
+
help="Install script file inside ARTIFACTS_FOLDER, or a single-line shell command.",
|
|
92
|
+
)
|
|
93
|
+
@click.option(
|
|
94
|
+
"--selectables",
|
|
95
|
+
help="Optional resources in 'name1:file1;name2:file2' format.",
|
|
96
|
+
)
|
|
97
|
+
@click.option(
|
|
98
|
+
"--exclude",
|
|
99
|
+
multiple=True,
|
|
100
|
+
help="Exclude artifact files whose relative path or filename contains this text. May be repeated.",
|
|
101
|
+
)
|
|
102
|
+
@click.option(
|
|
103
|
+
"--variant",
|
|
104
|
+
help="Optional metadata variant name. Writes metadata-<variant>.json instead of metadata.json.",
|
|
105
|
+
)
|
|
106
|
+
def build_package_metadata(artifacts_folder, name, version, description, install_script, selectables, exclude, variant):
|
|
107
|
+
"""
|
|
108
|
+
Generate metadata.json, or metadata-<variant>.json, for sima-cli package installation.
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
metadata = build_metadata(
|
|
112
|
+
artifacts_folder=artifacts_folder,
|
|
113
|
+
name=name,
|
|
114
|
+
version=version,
|
|
115
|
+
description=description,
|
|
116
|
+
install_script=install_script,
|
|
117
|
+
selectables=selectables,
|
|
118
|
+
exclude=exclude,
|
|
119
|
+
)
|
|
120
|
+
output_path = write_metadata(artifacts_folder, metadata, variant=variant)
|
|
121
|
+
except ValueError as exc:
|
|
122
|
+
raise click.ClickException(str(exc))
|
|
123
|
+
|
|
124
|
+
click.echo(f"✅ Package metadata written to: {output_path}")
|
|
125
|
+
|
|
77
126
|
# ------------------------------------------------------------------------------------------------------------
|
|
78
127
|
# Register the group to main CLI entrypoint, skip this group if it's running inside the SDK container already
|
|
79
128
|
# ------------------------------------------------------------------------------------------------------------
|
|
@@ -38,7 +38,7 @@ console = Console()
|
|
|
38
38
|
# ------------------------------------------------------------
|
|
39
39
|
# Group Definition
|
|
40
40
|
# ------------------------------------------------------------
|
|
41
|
-
@click.group(
|
|
41
|
+
@click.group(context_settings={"ignore_unknown_options": True, "allow_extra_args": True})
|
|
42
42
|
@click.option(
|
|
43
43
|
"-v", "--version",
|
|
44
44
|
"version_filter",
|
|
@@ -173,8 +173,19 @@ def launch_sdk_tool(tool: str, cmd, ctx):
|
|
|
173
173
|
is_flag=True,
|
|
174
174
|
help="Skip Model SDK extension setup. Intended for CI installation tests.",
|
|
175
175
|
)
|
|
176
|
+
@click.option(
|
|
177
|
+
"--minimal",
|
|
178
|
+
is_flag=True,
|
|
179
|
+
help="Skip optional Neat SDK container extras for CI compilation jobs.",
|
|
180
|
+
)
|
|
181
|
+
@click.option(
|
|
182
|
+
"--workspace",
|
|
183
|
+
type=click.Path(file_okay=False, dir_okay=True),
|
|
184
|
+
default=None,
|
|
185
|
+
help="Host workspace directory to mount into SDK containers instead of ~/workspace.",
|
|
186
|
+
)
|
|
176
187
|
@click.pass_context
|
|
177
|
-
def setup(ctx, yes, noninteractive, devkit, no_insight, no_model_sdk):
|
|
188
|
+
def setup(ctx, yes, noninteractive, devkit, no_insight, no_model_sdk, minimal, workspace):
|
|
178
189
|
"""Initialize SDK environment and select components to start."""
|
|
179
190
|
devkit_ip = _resolve_devkit_ip(devkit)
|
|
180
191
|
try:
|
|
@@ -184,6 +195,8 @@ def setup(ctx, yes, noninteractive, devkit, no_insight, no_model_sdk):
|
|
|
184
195
|
devkit_ip=devkit_ip,
|
|
185
196
|
no_insight=no_insight,
|
|
186
197
|
no_model_sdk=no_model_sdk,
|
|
198
|
+
minimal=minimal,
|
|
199
|
+
workspace=workspace,
|
|
187
200
|
)
|
|
188
201
|
except subprocess.CalledProcessError as e:
|
|
189
202
|
raise click.ClickException(f"SDK setup failed while running: {' '.join(e.cmd)}") from e
|