sima-cli 2.1.6__tar.gz → 2.1.8__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.6/sima_cli.egg-info → sima_cli-2.1.8}/PKG-INFO +18 -5
- {sima_cli-2.1.6 → sima_cli-2.1.8}/README.md +17 -4
- {sima_cli-2.1.6 → sima_cli-2.1.8}/pyproject.toml +1 -1
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/__version__.py +1 -1
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/cli.py +81 -5
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/metadata_installer.py +202 -8
- sima_cli-2.1.8/sima_cli/install/package_builder.py +308 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/registry.py +66 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/commands.py +1 -1
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/install.py +218 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/neat.py +37 -6
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/preinstall.py +10 -3
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/utils.py +4 -1
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/netboot.py +126 -24
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/updater.py +11 -16
- sima_cli-2.1.8/sima_cli/upgrade/selfupdate.py +408 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/vulcan/artifacts.py +99 -6
- sima_cli-2.1.8/sima_cli/vulcan/commands.py +304 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8/sima_cli.egg-info}/PKG-INFO +18 -5
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli.egg-info/SOURCES.txt +4 -0
- sima_cli-2.1.8/tests/unit/test_firmware.py +60 -0
- sima_cli-2.1.8/tests/unit/test_metadata_installer.py +207 -0
- sima_cli-2.1.8/tests/unit/test_netboot.py +98 -0
- sima_cli-2.1.8/tests/unit/test_package_builder.py +359 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_sdk_image_detection.py +281 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_sdk_preinstall.py +14 -0
- sima_cli-2.1.8/tests/unit/test_selfupdate.py +181 -0
- sima_cli-2.1.8/tests/unit/test_vulcan.py +524 -0
- sima_cli-2.1.6/sima_cli/upgrade/selfupdate.py +0 -236
- sima_cli-2.1.6/sima_cli/vulcan/commands.py +0 -115
- sima_cli-2.1.6/tests/unit/test_firmware.py +0 -0
- sima_cli-2.1.6/tests/unit/test_selfupdate.py +0 -68
- sima_cli-2.1.6/tests/unit/test_vulcan.py +0 -180
- {sima_cli-2.1.6 → sima_cli-2.1.8}/LICENSE +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/MANIFEST.in +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/requirements.txt +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/setup.cfg +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/setup.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/__main__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/app_zoo/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/app_zoo/app.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/app_zoo/commands.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/auth/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/auth/auth0.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/auth/devportal.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/auth/login.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/auth/oauth.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/data/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/data/resources_internal.yaml +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/data/resources_public.yaml +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/deploy_only/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/deploy_only/device/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/deploy_only/device/commands.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/deploy_only/mpk/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/deploy_only/mpk/commands.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/discover/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/discover/discover.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/discover/linuxll.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/download/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/download/downloader.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/github_assets.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/hostdriver.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/metadata_info.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/metadata_validator.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/optiview.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/install/palette.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/mla/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/mla/meminfo.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/model_zoo/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/model_zoo/model.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/network/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/network/network.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/playbooks/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/playbooks/commands.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/playbooks/manager.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/cmdexec.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/config.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/linux_shared_network.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/requirements.json +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/script.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/stop.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/sdk/uninstall.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/serial/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/serial/serial.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/storage/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/storage/nvme.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/storage/sdcard.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/bmaptool.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/bootimg.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/cleanlog.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/elxr.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/local.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/query.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/update/remote.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/upgrade/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/api_common.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/artifactory.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/common.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/config.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/config_loader.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/container_registries.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/device_api.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/disk.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/docker.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/env.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/errors.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/mpk_api.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/net.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/network.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/pcie.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/pkg_update_check.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/serializers.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/services.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/utils/tag.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli/vulcan/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli.egg-info/dependency_links.txt +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli.egg-info/entry_points.txt +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli.egg-info/requires.txt +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/sima_cli.egg-info/top_level.txt +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/e2e/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/__init__.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_app_zoo.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_auth.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_cli.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_cli_stdio.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_download.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_elxr_update.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_install_stub.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_model_zoo.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_pkg_update_check.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_sdk_uninstall.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_skills_commands.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/tests/unit/test_skills_manager.py +0 -0
- {sima_cli-2.1.6 → sima_cli-2.1.8}/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.8
|
|
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
|
|
@@ -163,10 +163,11 @@ sima-cli vulcan download --env production core main
|
|
|
163
163
|
- `dev`: `https://artifacts.neat.paconsultings.com`
|
|
164
164
|
- `staging`: `https://artifacts.stg.neat.sima.ai`
|
|
165
165
|
- `production`: `https://artifacts.neat.sima.ai`
|
|
166
|
-
- `
|
|
166
|
+
- `dev` and `staging` are available for Vulcan downloads. `production` is not yet available.
|
|
167
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]`
|
|
168
|
+
- `sima-cli vulcan --env {dev|stg|staging|prd|prod|production} download [REPO] [BRANCH_OR_TAG]`
|
|
169
|
+
- `sima-cli vulcan download --env {dev|stg|staging|prd|prod|production} [REPO] [BRANCH_OR_TAG]`
|
|
170
|
+
- Shortcut flags are also available: `--dev`, `--stg`/`--staging`, and `--prd`/`--prod`.
|
|
170
171
|
- If `REPO` is omitted, the CLI prompts for a repository.
|
|
171
172
|
- If `BRANCH_OR_TAG` is omitted, the CLI downloads `branches.json` and prompts for a branch.
|
|
172
173
|
- For automation, pass both values and add `--json` for structured output.
|
|
@@ -587,7 +588,19 @@ sima-cli selfupdate
|
|
|
587
588
|
sima-cli selfupdate --dev
|
|
588
589
|
```
|
|
589
590
|
|
|
590
|
-
- Update from
|
|
591
|
+
- Update from Vulcan dev artifacts. If `--branch` is omitted, sima-cli loads `branches.json` and prompts for a branch.
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
sima-cli selfupdate --stg --branch main
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
- Update from Vulcan staging artifacts.
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
sima-cli selfupdate --neat --branch main
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
- Update from Vulcan production artifacts. Production aliases are `--prd`, `--prod`, `--neat`, and `--vulcan`.
|
|
591
604
|
|
|
592
605
|
```bash
|
|
593
606
|
sima-cli selfupdate -v 0.0.46
|
|
@@ -129,10 +129,11 @@ sima-cli vulcan download --env production core main
|
|
|
129
129
|
- `dev`: `https://artifacts.neat.paconsultings.com`
|
|
130
130
|
- `staging`: `https://artifacts.stg.neat.sima.ai`
|
|
131
131
|
- `production`: `https://artifacts.neat.sima.ai`
|
|
132
|
-
- `
|
|
132
|
+
- `dev` and `staging` are available for Vulcan downloads. `production` is not yet available.
|
|
133
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]`
|
|
134
|
+
- `sima-cli vulcan --env {dev|stg|staging|prd|prod|production} download [REPO] [BRANCH_OR_TAG]`
|
|
135
|
+
- `sima-cli vulcan download --env {dev|stg|staging|prd|prod|production} [REPO] [BRANCH_OR_TAG]`
|
|
136
|
+
- Shortcut flags are also available: `--dev`, `--stg`/`--staging`, and `--prd`/`--prod`.
|
|
136
137
|
- If `REPO` is omitted, the CLI prompts for a repository.
|
|
137
138
|
- If `BRANCH_OR_TAG` is omitted, the CLI downloads `branches.json` and prompts for a branch.
|
|
138
139
|
- For automation, pass both values and add `--json` for structured output.
|
|
@@ -553,7 +554,19 @@ sima-cli selfupdate
|
|
|
553
554
|
sima-cli selfupdate --dev
|
|
554
555
|
```
|
|
555
556
|
|
|
556
|
-
- Update from
|
|
557
|
+
- Update from Vulcan dev artifacts. If `--branch` is omitted, sima-cli loads `branches.json` and prompts for a branch.
|
|
558
|
+
|
|
559
|
+
```bash
|
|
560
|
+
sima-cli selfupdate --stg --branch main
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
- Update from Vulcan staging artifacts.
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
sima-cli selfupdate --neat --branch main
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
- Update from Vulcan production artifacts. Production aliases are `--prd`, `--prod`, `--neat`, and `--vulcan`.
|
|
557
570
|
|
|
558
571
|
```bash
|
|
559
572
|
sima-cli selfupdate -v 0.0.46
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# sima_cli/__version__.py
|
|
2
|
-
__version__ = "2.1.
|
|
2
|
+
__version__ = "2.1.8"
|
|
@@ -30,6 +30,13 @@ 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
32
|
from sima_cli.vulcan import register_vulcan_commands
|
|
33
|
+
from sima_cli.vulcan.commands import (
|
|
34
|
+
ENV_METAVAR,
|
|
35
|
+
_environment_shortcut_options,
|
|
36
|
+
_normalize_environment_option,
|
|
37
|
+
_resolve_environment,
|
|
38
|
+
install_vulcan_package,
|
|
39
|
+
)
|
|
33
40
|
|
|
34
41
|
def _configure_stdio_errors() -> None:
|
|
35
42
|
for stream in (getattr(sys, "stdout", None), getattr(sys, "stderr", None)):
|
|
@@ -504,7 +511,39 @@ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
|
|
504
511
|
@click.argument("component", required=False)
|
|
505
512
|
@click.option("-v", "--version", help="SDK version (required for SDK-dependent components unless --metadata is provided)")
|
|
506
513
|
@click.option("-m", "--mirror", help="URL to a metadata.json file for generic installation")
|
|
507
|
-
@click.option("-t", "--tag", help="Tag of the package
|
|
514
|
+
@click.option("-t", "--tag", help="Tag of the package. With --neat, metadata variant type such as minimum.")
|
|
515
|
+
@click.option("--neat", "use_neat", is_flag=True, help="Install from Neat artifacts using the Neat package resolver.")
|
|
516
|
+
@click.option(
|
|
517
|
+
"--vulcan",
|
|
518
|
+
"use_vulcan",
|
|
519
|
+
is_flag=True,
|
|
520
|
+
help="Install from Neat/Vulcan artifacts. Compatibility alias for --neat.",
|
|
521
|
+
)
|
|
522
|
+
@_environment_shortcut_options
|
|
523
|
+
@click.option(
|
|
524
|
+
"--env",
|
|
525
|
+
"vulcan_environment",
|
|
526
|
+
metavar=ENV_METAVAR,
|
|
527
|
+
callback=_normalize_environment_option,
|
|
528
|
+
default=None,
|
|
529
|
+
help="Neat artifact environment. Used with --neat or --vulcan. Defaults to production.",
|
|
530
|
+
)
|
|
531
|
+
@click.option(
|
|
532
|
+
"--base-url",
|
|
533
|
+
"vulcan_base_url",
|
|
534
|
+
default=None,
|
|
535
|
+
envvar="SIMA_NEAT_BASE_URL",
|
|
536
|
+
help="Override the Neat artifact base URL. Used with --neat or --vulcan.",
|
|
537
|
+
)
|
|
538
|
+
@click.option(
|
|
539
|
+
"-d",
|
|
540
|
+
"--install-dir",
|
|
541
|
+
default=".",
|
|
542
|
+
show_default=True,
|
|
543
|
+
type=click.Path(file_okay=False, dir_okay=True, path_type=str),
|
|
544
|
+
help="Directory where Neat package resources are downloaded and installed. Used with --neat or --vulcan.",
|
|
545
|
+
)
|
|
546
|
+
@click.option("--json", "json_output", is_flag=True, help="With --neat or --vulcan, print resolved metadata URL and exit.")
|
|
508
547
|
@click.option(
|
|
509
548
|
"-f",
|
|
510
549
|
"--force",
|
|
@@ -513,7 +552,21 @@ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
|
|
513
552
|
help="Force installation even if compatibility checks fail.",
|
|
514
553
|
)
|
|
515
554
|
@click.pass_context
|
|
516
|
-
def install_cmd(
|
|
555
|
+
def install_cmd(
|
|
556
|
+
ctx,
|
|
557
|
+
component,
|
|
558
|
+
version,
|
|
559
|
+
mirror,
|
|
560
|
+
tag,
|
|
561
|
+
use_neat,
|
|
562
|
+
use_vulcan,
|
|
563
|
+
vulcan_environment,
|
|
564
|
+
environment_flag,
|
|
565
|
+
vulcan_base_url,
|
|
566
|
+
install_dir,
|
|
567
|
+
json_output,
|
|
568
|
+
force,
|
|
569
|
+
):
|
|
517
570
|
"""
|
|
518
571
|
Install SiMa packages.
|
|
519
572
|
|
|
@@ -548,12 +601,32 @@ def install_cmd(ctx, component, version, mirror, tag, force):
|
|
|
548
601
|
"""
|
|
549
602
|
internal = ctx.obj.get("internal", False)
|
|
550
603
|
|
|
604
|
+
use_neat_resolver = use_neat or use_vulcan
|
|
605
|
+
if use_neat_resolver:
|
|
606
|
+
if use_neat and use_vulcan:
|
|
607
|
+
raise click.ClickException("Use only one of --neat or --vulcan.")
|
|
608
|
+
if mirror:
|
|
609
|
+
raise click.ClickException("--mirror cannot be used with --neat.")
|
|
610
|
+
if not component:
|
|
611
|
+
raise click.ClickException("You must specify a Neat target when using --neat.")
|
|
612
|
+
install_vulcan_package(
|
|
613
|
+
target=component,
|
|
614
|
+
environment=_resolve_environment(vulcan_environment, environment_flag),
|
|
615
|
+
base_url=vulcan_base_url or os.getenv("SIMA_VULCAN_BASE_URL"),
|
|
616
|
+
package_type=tag,
|
|
617
|
+
install_dir=install_dir,
|
|
618
|
+
force=force,
|
|
619
|
+
json_output=json_output,
|
|
620
|
+
)
|
|
621
|
+
return None
|
|
622
|
+
|
|
551
623
|
# Metadata-based installation path
|
|
552
624
|
if mirror:
|
|
553
625
|
if component:
|
|
554
626
|
click.echo(f"⚠️ Component '{component}' is ignored when using --metadata. Proceeding with metadata-based installation.")
|
|
555
627
|
click.echo(f"🔧 Installing generic component from metadata URL: {mirror}")
|
|
556
|
-
|
|
628
|
+
install_from_metadata(metadata_url=mirror, internal=internal, force=force)
|
|
629
|
+
return None
|
|
557
630
|
|
|
558
631
|
# No component and no metadata: error
|
|
559
632
|
if not component:
|
|
@@ -562,11 +635,13 @@ def install_cmd(ctx, component, version, mirror, tag, force):
|
|
|
562
635
|
|
|
563
636
|
# if user specified gh: as component, treat it the same as -m
|
|
564
637
|
if component.startswith("gh:"):
|
|
565
|
-
|
|
638
|
+
install_from_metadata(metadata_url=component, internal=False, force=force)
|
|
639
|
+
return None
|
|
566
640
|
|
|
567
641
|
# if the user specified cr: or ghcr: as component, install from container registry
|
|
568
642
|
if component.startswith("cr:") or component.startswith("ghcr:"):
|
|
569
|
-
|
|
643
|
+
install_from_cr(resource_spec=component, internal=internal)
|
|
644
|
+
return None
|
|
570
645
|
|
|
571
646
|
version = resolve_version(version)
|
|
572
647
|
|
|
@@ -601,6 +676,7 @@ def install_cmd(ctx, component, version, mirror, tag, force):
|
|
|
601
676
|
ctx.exit(1)
|
|
602
677
|
|
|
603
678
|
click.echo("✅ Installation complete.")
|
|
679
|
+
return None
|
|
604
680
|
|
|
605
681
|
|
|
606
682
|
# ----------------------
|
|
@@ -11,7 +11,7 @@ import stat
|
|
|
11
11
|
import shlex
|
|
12
12
|
import platform
|
|
13
13
|
import hashlib
|
|
14
|
-
from urllib.parse import urlparse, quote, urljoin
|
|
14
|
+
from urllib.parse import urlparse, quote, urljoin, unquote
|
|
15
15
|
from typing import Dict
|
|
16
16
|
from tqdm import tqdm
|
|
17
17
|
from pathlib import Path
|
|
@@ -280,6 +280,169 @@ def _compute_sha256(file_path: Path, chunk_size: int = 1024 * 1024) -> str:
|
|
|
280
280
|
hasher.update(chunk)
|
|
281
281
|
return hasher.hexdigest()
|
|
282
282
|
|
|
283
|
+
def _resolve_resource_url(base_url: str, resource: str) -> str:
|
|
284
|
+
"""
|
|
285
|
+
Resolve a metadata resource to a downloadable URL.
|
|
286
|
+
|
|
287
|
+
Metadata resources are file names or relative paths. Encode each path segment
|
|
288
|
+
so URL-reserved characters that are valid in artifact names, such as '+', do
|
|
289
|
+
not get interpreted by object storage/CDNs as a different key.
|
|
290
|
+
"""
|
|
291
|
+
parsed_resource = urlparse(resource)
|
|
292
|
+
if parsed_resource.scheme or parsed_resource.netloc:
|
|
293
|
+
return resource
|
|
294
|
+
|
|
295
|
+
encoded_resource = "/".join(
|
|
296
|
+
quote(segment, safe="")
|
|
297
|
+
for segment in resource.split("/")
|
|
298
|
+
)
|
|
299
|
+
return urljoin(base_url, encoded_resource)
|
|
300
|
+
|
|
301
|
+
def _resolve_resource_url_candidates(base_url: str, resource: str) -> list[str]:
|
|
302
|
+
primary_url = _resolve_resource_url(base_url, resource)
|
|
303
|
+
|
|
304
|
+
parsed_resource = urlparse(resource)
|
|
305
|
+
if parsed_resource.scheme or parsed_resource.netloc or "%" not in resource:
|
|
306
|
+
return [primary_url]
|
|
307
|
+
|
|
308
|
+
percent_preserving_resource = "/".join(
|
|
309
|
+
quote(segment, safe="%")
|
|
310
|
+
for segment in resource.split("/")
|
|
311
|
+
)
|
|
312
|
+
fallback_url = urljoin(base_url, percent_preserving_resource)
|
|
313
|
+
if fallback_url == primary_url:
|
|
314
|
+
return [primary_url]
|
|
315
|
+
return [primary_url, fallback_url]
|
|
316
|
+
|
|
317
|
+
def _metadata_resource_path(dest_folder: str, resource: str, resource_url: str) -> Path:
|
|
318
|
+
parsed_resource = urlparse(resource)
|
|
319
|
+
if parsed_resource.scheme or parsed_resource.netloc:
|
|
320
|
+
file_name = os.path.basename(urlparse(resource_url).path)
|
|
321
|
+
else:
|
|
322
|
+
file_name = os.path.basename(resource)
|
|
323
|
+
|
|
324
|
+
if not file_name:
|
|
325
|
+
raise click.ClickException(f"❌ Cannot determine file name for resource '{resource}'.")
|
|
326
|
+
|
|
327
|
+
return Path(dest_folder) / file_name
|
|
328
|
+
|
|
329
|
+
def _normalize_downloaded_metadata_resource(local_path: str, expected_path: Path) -> str:
|
|
330
|
+
downloaded_path = Path(local_path)
|
|
331
|
+
if downloaded_path == expected_path:
|
|
332
|
+
return local_path
|
|
333
|
+
|
|
334
|
+
if expected_path.exists():
|
|
335
|
+
expected_path.unlink()
|
|
336
|
+
downloaded_path.rename(expected_path)
|
|
337
|
+
return str(expected_path)
|
|
338
|
+
|
|
339
|
+
def _download_metadata_file_resource(
|
|
340
|
+
resource: str,
|
|
341
|
+
resource_urls: list[str],
|
|
342
|
+
dest_folder: str,
|
|
343
|
+
dest_path: Path,
|
|
344
|
+
internal: bool,
|
|
345
|
+
) -> str:
|
|
346
|
+
errors: list[str] = []
|
|
347
|
+
for index, resource_url in enumerate(resource_urls):
|
|
348
|
+
try:
|
|
349
|
+
local_path = download_file_from_url(
|
|
350
|
+
url=resource_url,
|
|
351
|
+
dest_folder=dest_folder,
|
|
352
|
+
internal=internal
|
|
353
|
+
)
|
|
354
|
+
return _normalize_downloaded_metadata_resource(local_path, dest_path)
|
|
355
|
+
except Exception as e:
|
|
356
|
+
errors.append(f"{resource_url}: {e}")
|
|
357
|
+
if index < len(resource_urls) - 1:
|
|
358
|
+
click.echo(
|
|
359
|
+
f"⚠️ Download failed for encoded resource URL; retrying percent-preserving URL for '{resource}'."
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
raise click.ClickException("; ".join(errors))
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _resource_basename(resource: str) -> str:
|
|
366
|
+
parsed = urlparse(resource)
|
|
367
|
+
path = parsed.path if parsed.scheme or parsed.netloc else resource
|
|
368
|
+
return unquote(os.path.basename(path)).lower()
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _is_wheel_resource(resource: str) -> bool:
|
|
372
|
+
return _resource_basename(resource).endswith(".whl")
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _wheel_platform_tag(resource: str) -> str:
|
|
376
|
+
name = _resource_basename(resource)
|
|
377
|
+
if not name.endswith(".whl"):
|
|
378
|
+
return ""
|
|
379
|
+
parts = name[:-4].split("-")
|
|
380
|
+
if len(parts) < 5:
|
|
381
|
+
return ""
|
|
382
|
+
return parts[-1].lower()
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _current_wheel_platform() -> tuple:
|
|
386
|
+
system = platform.system().lower()
|
|
387
|
+
machine = platform.machine().lower()
|
|
388
|
+
|
|
389
|
+
if system == "darwin":
|
|
390
|
+
os_family = "mac"
|
|
391
|
+
elif system == "windows":
|
|
392
|
+
os_family = "windows"
|
|
393
|
+
elif system == "linux":
|
|
394
|
+
os_family = "linux"
|
|
395
|
+
else:
|
|
396
|
+
os_family = system
|
|
397
|
+
|
|
398
|
+
if machine in {"x86_64", "amd64"}:
|
|
399
|
+
arch_tokens = {"x86_64", "amd64"}
|
|
400
|
+
elif machine in {"aarch64", "arm64"}:
|
|
401
|
+
arch_tokens = {"aarch64", "arm64"}
|
|
402
|
+
elif machine in {"i386", "i686", "x86"}:
|
|
403
|
+
arch_tokens = {"i386", "i686", "x86", "win32"}
|
|
404
|
+
else:
|
|
405
|
+
arch_tokens = {machine} if machine else set()
|
|
406
|
+
|
|
407
|
+
return os_family, arch_tokens
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _wheel_arch_matches(platform_tag: str, arch_tokens: set) -> bool:
|
|
411
|
+
known_arch_tokens = {"x86_64", "amd64", "aarch64", "arm64", "i386", "i686", "x86", "win32"}
|
|
412
|
+
present_tokens = {token for token in known_arch_tokens if token in platform_tag}
|
|
413
|
+
return not present_tokens or bool(present_tokens & arch_tokens)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _is_compatible_wheel_resource(resource: str) -> bool:
|
|
417
|
+
platform_tag = _wheel_platform_tag(resource)
|
|
418
|
+
if not platform_tag:
|
|
419
|
+
return True
|
|
420
|
+
if platform_tag in {"any", "none"}:
|
|
421
|
+
return True
|
|
422
|
+
|
|
423
|
+
os_family, arch_tokens = _current_wheel_platform()
|
|
424
|
+
if os_family == "mac":
|
|
425
|
+
os_matches = platform_tag.startswith("macosx") or "macos" in platform_tag
|
|
426
|
+
elif os_family == "windows":
|
|
427
|
+
os_matches = platform_tag.startswith("win") or platform_tag in {"win32", "win_amd64", "win_arm64"}
|
|
428
|
+
elif os_family == "linux":
|
|
429
|
+
os_matches = "linux" in platform_tag
|
|
430
|
+
else:
|
|
431
|
+
os_matches = False
|
|
432
|
+
|
|
433
|
+
return os_matches and _wheel_arch_matches(platform_tag, arch_tokens)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _filter_download_compatible_resources(resources: list) -> list:
|
|
437
|
+
filtered = []
|
|
438
|
+
for resource in resources:
|
|
439
|
+
if _is_wheel_resource(resource) and not _is_compatible_wheel_resource(resource):
|
|
440
|
+
click.echo(f"⏭️ Skipping incompatible wheel for this platform: {resource}")
|
|
441
|
+
continue
|
|
442
|
+
filtered.append(resource)
|
|
443
|
+
return filtered
|
|
444
|
+
|
|
445
|
+
|
|
283
446
|
def _download_assets(metadata: dict, base_url: str, dest_folder: str, internal: bool = False, skip_models: bool = False, tag: str = None) -> list:
|
|
284
447
|
"""
|
|
285
448
|
Downloads resources defined in metadata to a local destination folder.
|
|
@@ -321,8 +484,11 @@ def _download_assets(metadata: dict, base_url: str, dest_folder: str, internal:
|
|
|
321
484
|
continue
|
|
322
485
|
filtered_resources.append(r)
|
|
323
486
|
|
|
487
|
+
if metadata.get("download-compatible-files-only"):
|
|
488
|
+
filtered_resources = _filter_download_compatible_resources(filtered_resources)
|
|
489
|
+
|
|
324
490
|
if not filtered_resources:
|
|
325
|
-
click.echo("ℹ️ No
|
|
491
|
+
click.echo("ℹ️ No compatible resources to download.")
|
|
326
492
|
return []
|
|
327
493
|
|
|
328
494
|
click.echo(f"📥 Downloading {len(filtered_resources)} resource(s) to: {dest_folder}\n")
|
|
@@ -387,10 +553,8 @@ def _download_assets(metadata: dict, base_url: str, dest_folder: str, internal:
|
|
|
387
553
|
continue
|
|
388
554
|
|
|
389
555
|
# 🌐 Standard file or URL
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
file_name = os.path.basename(parsed.path)
|
|
393
|
-
dest_path = Path(dest_folder) / file_name
|
|
556
|
+
resource_urls = _resolve_resource_url_candidates(base_url, resource)
|
|
557
|
+
dest_path = _metadata_resource_path(dest_folder, resource, resource_urls[0])
|
|
394
558
|
|
|
395
559
|
if expected_sha256 and dest_path.exists() and dest_path.is_file():
|
|
396
560
|
existing_sha = _compute_sha256(dest_path)
|
|
@@ -398,9 +562,11 @@ def _download_assets(metadata: dict, base_url: str, dest_folder: str, internal:
|
|
|
398
562
|
click.echo(f"♻️ Checksum mismatch for existing file '{dest_path.name}', re-downloading.")
|
|
399
563
|
dest_path.unlink()
|
|
400
564
|
|
|
401
|
-
local_path =
|
|
402
|
-
|
|
565
|
+
local_path = _download_metadata_file_resource(
|
|
566
|
+
resource=resource,
|
|
567
|
+
resource_urls=resource_urls,
|
|
403
568
|
dest_folder=dest_folder,
|
|
569
|
+
dest_path=dest_path,
|
|
404
570
|
internal=internal
|
|
405
571
|
)
|
|
406
572
|
if expected_sha256:
|
|
@@ -417,6 +583,28 @@ def _download_assets(metadata: dict, base_url: str, dest_folder: str, internal:
|
|
|
417
583
|
|
|
418
584
|
return local_paths
|
|
419
585
|
|
|
586
|
+
def _mark_install_script_executable(metadata: Dict, install_dir: str) -> None:
|
|
587
|
+
script = metadata.get("installation", {}).get("script", "")
|
|
588
|
+
if not isinstance(script, str):
|
|
589
|
+
return
|
|
590
|
+
|
|
591
|
+
script = script.strip()
|
|
592
|
+
if not script or any(char in script for char in "\n\r;&|`$<>"):
|
|
593
|
+
return
|
|
594
|
+
|
|
595
|
+
script_path = Path(script)
|
|
596
|
+
if not script_path.is_absolute():
|
|
597
|
+
script_path = Path(install_dir) / script_path
|
|
598
|
+
try:
|
|
599
|
+
resolved = script_path.resolve()
|
|
600
|
+
install_root = Path(install_dir).resolve()
|
|
601
|
+
if resolved != install_root and install_root not in resolved.parents:
|
|
602
|
+
return
|
|
603
|
+
if resolved.is_file():
|
|
604
|
+
resolved.chmod(resolved.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
605
|
+
except OSError:
|
|
606
|
+
return
|
|
607
|
+
|
|
420
608
|
def selectable_resource_handler(metadata):
|
|
421
609
|
"""
|
|
422
610
|
Allow user to select one or more opt-in resources to download.
|
|
@@ -936,6 +1124,10 @@ def _is_platform_compatible(metadata: dict, force: bool = False) -> bool:
|
|
|
936
1124
|
platforms = metadata.get("platforms", [])
|
|
937
1125
|
board_ver, _ = get_sima_build_version()
|
|
938
1126
|
|
|
1127
|
+
if not platforms:
|
|
1128
|
+
click.echo("ℹ️ No platform restrictions specified; treating package as compatible with all platforms.")
|
|
1129
|
+
return True
|
|
1130
|
+
|
|
939
1131
|
# Detect current OS and version
|
|
940
1132
|
os_name = platform.system().lower() # 'darwin', 'windows', 'linux'
|
|
941
1133
|
os_version = "Unknown"
|
|
@@ -1218,8 +1410,10 @@ def install_from_metadata(metadata_url: str, internal: bool, install_dir: str =
|
|
|
1218
1410
|
if _is_platform_compatible(metadata, force) or force:
|
|
1219
1411
|
local_paths = _download_assets(metadata, metadata_url, install_dir, internal, tag=tag)
|
|
1220
1412
|
if len(local_paths) > 0:
|
|
1413
|
+
_mark_install_script_executable(metadata, install_dir)
|
|
1221
1414
|
_combine_multipart_files(install_dir, local_paths=local_paths)
|
|
1222
1415
|
_extract_archives_in_folder(install_dir, local_paths)
|
|
1416
|
+
_mark_install_script_executable(metadata, install_dir)
|
|
1223
1417
|
_run_installation_script(metadata=metadata, extract_path=install_dir)
|
|
1224
1418
|
|
|
1225
1419
|
except Exception as e:
|