systemlink-cli 1.11.1__tar.gz → 1.11.4__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.11.1 → systemlink_cli-1.11.4}/PKG-INFO +3 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/pyproject.toml +15 -11
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/_version.py +1 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/main.py +3 -6
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/notebook_click.py +0 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/platform.py +0 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/rich_output.py +0 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/routine_click.py +0 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/nipkg-file-package/SKILL.md +79 -33
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/system_click.py +1 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/user_click.py +0 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/web_editor.py +62 -11
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/workitem_click.py +0 -1
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/LICENSE +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/dff-editor/index.html +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/__init__.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/__main__.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/config.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/config_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/example_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/demo-complete-workflow/README.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/demo-test-plans/README.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/demo-test-plans/config.yaml +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/file_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/function_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/mcp_reachability.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/profiles.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skill_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/SKILL.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/commands.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/troubleshooting.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-job-debugging/SKILL.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-notebook/SKILL.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-notebook/references/interfaces.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-notebook/references/notebook-patterns.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-python-test/SKILL.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/spec_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/system_query_utils.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/utils.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/webapp_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: systemlink-cli
|
|
3
|
-
Version: 1.11.
|
|
3
|
+
Version: 1.11.4
|
|
4
4
|
Summary: SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Fred Visser
|
|
@@ -11,8 +11,10 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
13
|
Requires-Dist: click (>=7.1.2)
|
|
14
|
+
Requires-Dist: cryptography (>=46.0.7)
|
|
14
15
|
Requires-Dist: keyring (>=25.6.0,<26.0.0)
|
|
15
16
|
Requires-Dist: packaging (>=21.0)
|
|
17
|
+
Requires-Dist: pygments (>=2.20.0)
|
|
16
18
|
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
17
19
|
Requires-Dist: questionary (>=2.1.1,<3.0.0)
|
|
18
20
|
Requires-Dist: requests (>=2.32.4,<3.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.11.
|
|
3
|
+
version = "1.11.4"
|
|
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" }]
|
|
@@ -14,6 +14,7 @@ include = [
|
|
|
14
14
|
[tool.poetry.scripts]
|
|
15
15
|
slcli = "slcli.__main__:cli"
|
|
16
16
|
slcli-mcp = "slcli.mcp_server:main"
|
|
17
|
+
ni-python-styleguide = "scripts.styleguide:main"
|
|
17
18
|
build-pyinstaller = "scripts.build_pyinstaller:main"
|
|
18
19
|
update-version = "scripts.update_version:main"
|
|
19
20
|
release-from-changes = "scripts.towncrier_release:main"
|
|
@@ -39,13 +40,25 @@ questionary = "^2.1.1"
|
|
|
39
40
|
packaging = ">=21.0"
|
|
40
41
|
rich = ">=13.7,<15"
|
|
41
42
|
rich-click = ">=1.8,<2"
|
|
43
|
+
cryptography = ">=46.0.7"
|
|
44
|
+
pygments = ">=2.20.0"
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
[tool.poetry.group.dev.dependencies]
|
|
45
48
|
# Lint
|
|
46
|
-
ni-python-styleguide = ">=0.4.3"
|
|
47
49
|
cyclonedx-bom = "^7.0.0"
|
|
48
50
|
setuptools = "<81" # pin to retain pkg_resources used by flake8-import-order
|
|
51
|
+
black = ">=26.3.1"
|
|
52
|
+
flake8 = ">=7.3.0"
|
|
53
|
+
flake8-black = ">=0.4.0"
|
|
54
|
+
flake8-docstrings = ">=1.7.0"
|
|
55
|
+
flake8-import-order = ">=0.19.2"
|
|
56
|
+
isort = ">=6.0.1,<9"
|
|
57
|
+
pep8-naming = ">=0.15.1"
|
|
58
|
+
lxml = ">=6.1.0"
|
|
59
|
+
pyjwt = ">=2.12.0"
|
|
60
|
+
python-dotenv = ">=1.2.2"
|
|
61
|
+
python-multipart = ">=0.0.26"
|
|
49
62
|
|
|
50
63
|
# Type checking
|
|
51
64
|
mypy = ">=1.0"
|
|
@@ -96,15 +109,6 @@ markers = [
|
|
|
96
109
|
[tool.black]
|
|
97
110
|
line-length = 100
|
|
98
111
|
|
|
99
|
-
[tool.ni-python-styleguide]
|
|
100
|
-
# D301 ("Use r""" if any backslashes in a docstring") is suppressed because
|
|
101
|
-
# Click uses \b (backspace chr(8)) as a paragraph-formatting marker in docstrings
|
|
102
|
-
# to prevent line-wrapping in --help output. This requires a regular """ string;
|
|
103
|
-
# using r""" passes the literal two-char sequence \b which Click does not recognise.
|
|
104
|
-
|
|
105
|
-
[tool.ni-python-styleguide.lint]
|
|
106
|
-
extend_ignore = "D301"
|
|
107
|
-
|
|
108
112
|
[tool.mypy]
|
|
109
113
|
python_version = "3.13"
|
|
110
114
|
files = "**/*.py"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""slcli entry points."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import tomllib
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from types import ModuleType
|
|
6
7
|
from typing import Optional
|
|
@@ -8,7 +9,6 @@ from typing import Optional
|
|
|
8
9
|
import click as base_click
|
|
9
10
|
import keyring
|
|
10
11
|
import questionary
|
|
11
|
-
import tomllib
|
|
12
12
|
|
|
13
13
|
from .asset_click import register_asset_commands
|
|
14
14
|
from .comment_click import register_comment_commands
|
|
@@ -21,13 +21,10 @@ from .file_click import register_file_commands
|
|
|
21
21
|
from .function_click import register_function_commands
|
|
22
22
|
from .mcp_click import register_mcp_commands
|
|
23
23
|
from .notebook_click import register_notebook_commands
|
|
24
|
-
from .platform import
|
|
25
|
-
get_platform_info,
|
|
26
|
-
)
|
|
24
|
+
from .platform import get_platform_info
|
|
27
25
|
from .policy_click import register_policy_commands
|
|
28
26
|
from .profiles import set_profile_override
|
|
29
|
-
from .rich_output import install_rich_output
|
|
30
|
-
from .rich_output import render_table
|
|
27
|
+
from .rich_output import install_rich_output, render_table
|
|
31
28
|
from .routine_click import register_routine_commands
|
|
32
29
|
from .skill_click import register_skill_commands
|
|
33
30
|
from .spec_click import register_spec_commands
|
|
@@ -224,23 +224,25 @@ dependencies, create a Salt state file (`deploy/install.sls`) and apply it throu
|
|
|
224
224
|
SystemLink Systems Manager. A typical SLS for a Python test package covers:
|
|
225
225
|
|
|
226
226
|
1. **Download and install Python** — use the official Windows installer with `/quiet`,
|
|
227
|
-
`InstallAllUsers=1`, `PrependPath=1`.
|
|
227
|
+
`InstallAllUsers=1`, `PrependPath=1`. For `TargetDir`, pass the full
|
|
228
|
+
`key=value` as one quoted argument when using `Program Files`:
|
|
228
229
|
```yaml
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
230
|
+
install-python:
|
|
231
|
+
cmd.run:
|
|
232
|
+
- name: >-
|
|
233
|
+
"C:\Windows\Temp\python-3.12.9-amd64.exe"
|
|
234
|
+
/quiet
|
|
235
|
+
InstallAllUsers=1
|
|
236
|
+
PrependPath=1
|
|
237
|
+
"TargetDir=C:\Program Files\Python312"
|
|
238
|
+
Include_launcher=1
|
|
239
|
+
- shell: cmd
|
|
240
|
+
- unless: >-
|
|
241
|
+
powershell -Command "$r64 = Get-ChildItem 'HKLM:\SOFTWARE\Python\PythonCore' -ErrorAction SilentlyContinue; if ($r64) { exit 0 }; $r32 = Get-ChildItem 'HKLM:\SOFTWARE\WOW6432Node\Python\PythonCore' -ErrorAction SilentlyContinue; if ($r32) { exit 0 }; if (Test-Path 'C:\Program Files\Python312\python.exe') { exit 0 }; exit 1"
|
|
239
242
|
```
|
|
240
|
-
**Critical**:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
the value portion (`TargetDir="C:\Program Files\Python312"`).
|
|
243
|
+
**Critical**: In SystemLink/Salt `cmd.run` contexts, keep `TargetDir` as a single
|
|
244
|
+
quoted argument (for example `"TargetDir=C:\Program Files\Python312"`) and verify
|
|
245
|
+
install path checks.
|
|
244
246
|
|
|
245
247
|
2. **Add Python to PATH** — `win_path.exists` for both the install dir and `Scripts\`.
|
|
246
248
|
|
|
@@ -326,26 +328,69 @@ SystemLink Systems Manager. A typical SLS for a Python test package covers:
|
|
|
326
328
|
|
|
327
329
|
## Auto-Versioning Build Script Pattern
|
|
328
330
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
+
Use an incrementing build counter stored in `package/build_number.txt` alongside a base
|
|
332
|
+
version in `package/version.txt`. This produces short, valid Debian version strings
|
|
333
|
+
(`major.minor.patch.build`) that NI Package Manager and SystemLink accept reliably.
|
|
334
|
+
|
|
335
|
+
**Why not timestamps?** A timestamp-based suffix like `1.0.0.20260420083348` exceeds the
|
|
336
|
+
length that SystemLink's `pkg.installed` Salt state can resolve correctly. The feed lookup
|
|
337
|
+
fails silently and the state reports the package as not found. Always use a short numeric
|
|
338
|
+
build counter instead.
|
|
339
|
+
|
|
340
|
+
### File layout
|
|
341
|
+
|
|
342
|
+
```text
|
|
343
|
+
package/
|
|
344
|
+
├── version.txt # base version, e.g. "1.0.1" (edit when bumping major/minor/patch)
|
|
345
|
+
├── build_number.txt # next build number to use, e.g. "0" (auto-incremented by build script)
|
|
346
|
+
├── control
|
|
347
|
+
├── instructions
|
|
348
|
+
├── postinstall.bat
|
|
349
|
+
└── preuninstall.bat
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
If your build script stamps versions through `control.template`, keep
|
|
353
|
+
`control.template` in source control as the input template and write the stamped
|
|
354
|
+
result to `control` during the build.
|
|
355
|
+
|
|
356
|
+
### Build script snippet
|
|
331
357
|
|
|
332
358
|
```bat
|
|
333
|
-
|
|
334
|
-
|
|
359
|
+
set SCRIPT_DIR=%~dp0
|
|
360
|
+
set CONTROL_DIR=%SCRIPT_DIR%build\nipkg\control
|
|
361
|
+
set DEPLOY_SLS=%SCRIPT_DIR%deploy\install.sls
|
|
362
|
+
set VERSION_FILE=%SCRIPT_DIR%package\version.txt
|
|
363
|
+
set BUILD_NUMBER_FILE=%SCRIPT_DIR%package\build_number.txt
|
|
364
|
+
set BASE_VERSION=
|
|
365
|
+
set NEXT_BUILD=
|
|
366
|
+
|
|
367
|
+
set /p BASE_VERSION=<"%VERSION_FILE%"
|
|
368
|
+
set /p NEXT_BUILD=<"%BUILD_NUMBER_FILE%"
|
|
369
|
+
if "%BASE_VERSION%"=="" (
|
|
370
|
+
echo Failed to read package\version.txt.
|
|
371
|
+
exit /b 1
|
|
372
|
+
)
|
|
373
|
+
if "%NEXT_BUILD%"=="" set NEXT_BUILD=0
|
|
374
|
+
set PACKAGE_VERSION=%BASE_VERSION%.%NEXT_BUILD%
|
|
375
|
+
set /a WRITE_BUILD=%NEXT_BUILD%+1
|
|
376
|
+
echo %WRITE_BUILD%>"%BUILD_NUMBER_FILE%"
|
|
377
|
+
echo Version for this build: %PACKAGE_VERSION%
|
|
335
378
|
|
|
336
379
|
REM Stamp version into control file
|
|
337
380
|
powershell -NoProfile -Command ^
|
|
338
|
-
"(Get-Content -Raw
|
|
339
|
-
| Set-Content -Encoding ASCII '%CONTROL_DIR%\control'"
|
|
381
|
+
"$p='%CONTROL_DIR%\control.template'; $o='%CONTROL_DIR%\control'; (Get-Content -Raw $p) -replace '(?m)^Version:\s*.*$','Version: %PACKAGE_VERSION%' | Set-Content -Encoding ASCII $o"
|
|
340
382
|
|
|
341
383
|
REM Stamp version into deploy\install.sls (keeps SLS in sync with feed)
|
|
342
384
|
powershell -NoProfile -Command ^
|
|
343
|
-
"(Get-Content -Raw
|
|
344
|
-
| Set-Content -Encoding ASCII '%DEPLOY_SLS%'"
|
|
385
|
+
"$p='%DEPLOY_SLS%'; (Get-Content -Raw $p) -replace '(?m)^\s*-\s*my-package:\s*.*$',' - my-package: %PACKAGE_VERSION%' | Set-Content -Encoding ASCII $p"
|
|
345
386
|
```
|
|
346
387
|
|
|
347
|
-
|
|
348
|
-
|
|
388
|
+
### Workflow
|
|
389
|
+
|
|
390
|
+
1. On the first build: `version.txt` = `1.0.1`, `build_number.txt` = `0` → produces `1.0.1.0`, writes `1` back.
|
|
391
|
+
2. On the next build: reads `1`, produces `1.0.1.1`, writes `2` back, and so on.
|
|
392
|
+
3. To bump major/minor/patch: edit `version.txt` and reset `build_number.txt` to `0`.
|
|
393
|
+
4. Commit both files so the counter is shared across machines / CI runs.
|
|
349
394
|
|
|
350
395
|
## Complete Working SLS Example
|
|
351
396
|
|
|
@@ -370,7 +415,7 @@ install-python:
|
|
|
370
415
|
/quiet
|
|
371
416
|
InstallAllUsers=1
|
|
372
417
|
PrependPath=1
|
|
373
|
-
TargetDir=C:\
|
|
418
|
+
"TargetDir=C:\Program Files\Python312"
|
|
374
419
|
Include_launcher=1
|
|
375
420
|
- shell: cmd
|
|
376
421
|
- unless: >-
|
|
@@ -407,7 +452,7 @@ install-my-test-package:
|
|
|
407
452
|
pkg.installed:
|
|
408
453
|
- install_recommends: true
|
|
409
454
|
- pkgs:
|
|
410
|
-
- my-package: 1.0.0
|
|
455
|
+
- my-package: 1.0.1.0
|
|
411
456
|
- require:
|
|
412
457
|
- module: add-my-test-feed
|
|
413
458
|
|
|
@@ -443,9 +488,9 @@ install-pip-deps:
|
|
|
443
488
|
```
|
|
444
489
|
|
|
445
490
|
**Key rules from testing:**
|
|
446
|
-
- `TargetDir`
|
|
447
|
-
|
|
448
|
-
|
|
491
|
+
- Pass `TargetDir` as one quoted key/value argument when using `Program Files`
|
|
492
|
+
(`"TargetDir=C:\Program Files\Python312"`) and keep explicit registry/path checks
|
|
493
|
+
in `unless` guards.
|
|
449
494
|
- `unless` guards for Python install must check the registry AND the file path; using
|
|
450
495
|
only `python --version` can match the Salt minion's bundled Python, not the system one.
|
|
451
496
|
- The `require` type for `pkg.installed` after `module.run` is `module:`, not `pkgrepo:`.
|
|
@@ -464,7 +509,7 @@ file server-side and rejects anything that is not parseable as YAML.
|
|
|
464
509
|
supported**. Hardcode all values (Python version, paths, URLs) directly.
|
|
465
510
|
- **Validate locally** before uploading: `python -c "import yaml; yaml.safe_load(open('install.sls'))"`
|
|
466
511
|
- **Salt state functions that work**: `cmd.run`, `file.managed`, `file.serialize`,
|
|
467
|
-
`file.absent`, `win_path.exists`, `system.reboot`, `
|
|
512
|
+
`file.absent`, `win_path.exists`, `system.reboot`, `module.run`, `pkg.installed`
|
|
468
513
|
— any valid Salt state module is accepted as long as the YAML parses.
|
|
469
514
|
|
|
470
515
|
### Uploading States to SystemLink
|
|
@@ -474,8 +519,9 @@ The SystemLink Systems State API (`/nisystemsstate/v1/`) provides two ways to cr
|
|
|
474
519
|
#### Option 1: JSON API — Package/Feed States Only
|
|
475
520
|
|
|
476
521
|
`POST /nisystemsstate/v1/states` with a JSON body. This only supports `packages` and
|
|
477
|
-
`feeds` arrays
|
|
478
|
-
|
|
522
|
+
`feeds` arrays for package/feed-only workflows. Use this when the state only needs to
|
|
523
|
+
install nipkg packages. For custom SLS uploads, continue using the verified
|
|
524
|
+
`module.run` + `pkg.mod_repo` pattern.
|
|
479
525
|
|
|
480
526
|
```python
|
|
481
527
|
state = {
|
|
@@ -17,6 +17,48 @@ import requests
|
|
|
17
17
|
from .utils import ExitCodes, get_base_url, get_headers, get_ssl_verify
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def _validated_proxy_origin(api_base: str) -> tuple[str, str]:
|
|
21
|
+
"""Return a validated scheme and netloc for proxy requests.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
api_base: Configured SystemLink base URL.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Tuple of scheme and network location.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
ValueError: If the configured base URL is not a plain HTTP(S) origin.
|
|
31
|
+
"""
|
|
32
|
+
parsed = urllib.parse.urlsplit(api_base)
|
|
33
|
+
if parsed.scheme not in {"http", "https"}:
|
|
34
|
+
raise ValueError("Editor proxy requires an HTTP(S) SystemLink base URL")
|
|
35
|
+
if not parsed.netloc or parsed.username or parsed.password:
|
|
36
|
+
raise ValueError("Editor proxy requires a base URL without embedded credentials")
|
|
37
|
+
if parsed.path not in {"", "/"} or parsed.query or parsed.fragment:
|
|
38
|
+
raise ValueError("Editor proxy requires a base URL without path, query, or fragment")
|
|
39
|
+
return parsed.scheme, parsed.netloc
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _build_proxy_url(
|
|
43
|
+
origin_scheme: str,
|
|
44
|
+
origin_netloc: str,
|
|
45
|
+
target_path: str,
|
|
46
|
+
query: str = "",
|
|
47
|
+
) -> str:
|
|
48
|
+
"""Build a proxy URL from a validated origin and allowlisted path."""
|
|
49
|
+
return urllib.parse.urlunsplit((origin_scheme, origin_netloc, target_path, query, ""))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _validated_proxy_path(request_path: str) -> str:
|
|
53
|
+
"""Return a decoded absolute proxy path without dot-segments."""
|
|
54
|
+
decoded_path = urllib.parse.unquote(request_path)
|
|
55
|
+
if not decoded_path.startswith("/"):
|
|
56
|
+
raise ValueError("Editor proxy requires an absolute request path")
|
|
57
|
+
if any(segment in {".", ".."} for segment in decoded_path.split("/")):
|
|
58
|
+
raise ValueError("Editor proxy rejects paths containing dot-segments")
|
|
59
|
+
return decoded_path
|
|
60
|
+
|
|
61
|
+
|
|
20
62
|
class DFFWebEditor:
|
|
21
63
|
"""Web-based editor for custom fields configurations."""
|
|
22
64
|
|
|
@@ -132,6 +174,7 @@ class DFFWebEditor:
|
|
|
132
174
|
editor_dir = self._editor_dir # Capture for closure
|
|
133
175
|
temp_path = self._temp_path # Capture for closure
|
|
134
176
|
api_base = get_base_url().rstrip("/")
|
|
177
|
+
api_scheme, api_netloc = _validated_proxy_origin(api_base)
|
|
135
178
|
default_headers = get_headers()
|
|
136
179
|
ssl_verify = get_ssl_verify()
|
|
137
180
|
secret = self._secret
|
|
@@ -149,9 +192,14 @@ class DFFWebEditor:
|
|
|
149
192
|
|
|
150
193
|
def _proxy_request(self, method: str) -> bool:
|
|
151
194
|
parsed = urllib.parse.urlparse(self.path)
|
|
195
|
+
try:
|
|
196
|
+
request_path = _validated_proxy_path(parsed.path)
|
|
197
|
+
except ValueError as exc:
|
|
198
|
+
self.send_error(400, str(exc))
|
|
199
|
+
return True
|
|
152
200
|
|
|
153
201
|
# Serve slcli-config.json from temp directory
|
|
154
|
-
if
|
|
202
|
+
if request_path == "/slcli-config.json" and method == "GET":
|
|
155
203
|
config_file = temp_path / "slcli-config.json"
|
|
156
204
|
if config_file.exists():
|
|
157
205
|
self.send_response(200)
|
|
@@ -164,7 +212,7 @@ class DFFWebEditor:
|
|
|
164
212
|
return True
|
|
165
213
|
|
|
166
214
|
# Serve config.json (the DFF configuration) from temp directory
|
|
167
|
-
if
|
|
215
|
+
if request_path == "/config.json" and method == "GET":
|
|
168
216
|
config_file = temp_path / "config.json"
|
|
169
217
|
if config_file.exists():
|
|
170
218
|
self.send_response(200)
|
|
@@ -182,12 +230,12 @@ class DFFWebEditor:
|
|
|
182
230
|
"/api/dff/update-configurations": "/nidynamicformfields/v1/update-configurations",
|
|
183
231
|
}
|
|
184
232
|
|
|
185
|
-
if
|
|
186
|
-
target_path = path_map[
|
|
187
|
-
elif
|
|
188
|
-
target_path =
|
|
189
|
-
elif
|
|
190
|
-
target_path =
|
|
233
|
+
if request_path in path_map:
|
|
234
|
+
target_path = path_map[request_path]
|
|
235
|
+
elif request_path.startswith("/nidynamicformfields/v1/"):
|
|
236
|
+
target_path = request_path
|
|
237
|
+
elif request_path.startswith("/niuser/v1/workspaces"):
|
|
238
|
+
target_path = request_path
|
|
191
239
|
else:
|
|
192
240
|
return False
|
|
193
241
|
|
|
@@ -197,9 +245,12 @@ class DFFWebEditor:
|
|
|
197
245
|
self.send_error(403, "Forbidden: Missing or invalid editor secret")
|
|
198
246
|
return True
|
|
199
247
|
|
|
200
|
-
target_url =
|
|
201
|
-
|
|
202
|
-
|
|
248
|
+
target_url = _build_proxy_url(
|
|
249
|
+
origin_scheme=api_scheme,
|
|
250
|
+
origin_netloc=api_netloc,
|
|
251
|
+
target_path=target_path,
|
|
252
|
+
query=parsed.query,
|
|
253
|
+
)
|
|
203
254
|
|
|
204
255
|
headers = dict(default_headers)
|
|
205
256
|
data = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/demo-complete-workflow/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/demo-complete-workflow/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/exercise-7-1-test-plans/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/exercise-7-1-test-plans/config.yaml
RENAMED
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/examples/spec-compliance-notebooks/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/analysis-recipes.md
RENAMED
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/datasheet-workflow.md
RENAMED
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/slcli/references/troubleshooting.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-job-debugging/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.1 → systemlink_cli-1.11.4}/slcli/skills/systemlink-python-test/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|