redeploy 0.2.41__tar.gz → 0.2.43__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.
- {redeploy-0.2.41 → redeploy-0.2.43}/PKG-INFO +157 -6
- {redeploy-0.2.41 → redeploy-0.2.43}/README.md +155 -5
- {redeploy-0.2.41 → redeploy-0.2.43}/pyproject.toml +2 -1
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/__init__.py +1 -1
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/executor.py +4 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/handlers.py +134 -0
- redeploy-0.2.43/redeploy/apply/state_apply.py +222 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/__init__.py +2 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/blueprint.py +31 -1
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/device_map.py +11 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/hardware.py +42 -1
- redeploy-0.2.43/redeploy/cli/commands/push.py +110 -0
- redeploy-0.2.43/redeploy/hardware/kiosk/__init__.py +18 -0
- redeploy-0.2.43/redeploy/hardware/kiosk/autostart.py +84 -0
- redeploy-0.2.43/redeploy/hardware/kiosk/browsers.py +78 -0
- redeploy-0.2.43/redeploy/hardware/kiosk/compositors.py +80 -0
- redeploy-0.2.43/redeploy/hardware/kiosk/output_profiles.py +68 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/models.py +3 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy.egg-info/PKG-INFO +157 -6
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy.egg-info/SOURCES.txt +8 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy.egg-info/requires.txt +1 -0
- redeploy-0.2.43/tests/test_hardware_kiosk.py +230 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/exceptions.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/progress.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/rollback.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/state.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/utils/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/apply/utils/run_container_build.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/audit.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/blueprint/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/blueprint/extractor.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/blueprint/generators/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/blueprint/generators/docker_compose.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/blueprint/generators/migration.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/audit.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/detect.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/devices.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/diagnose.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/diff.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/exec_.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/export.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/import_.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/init.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/inspect.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/patterns.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/plan_apply.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/plugin.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/probe.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/state.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/status.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/target.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/version/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/version/commands.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/version/helpers.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/version/monorepo.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/version/release.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/version/scanner.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/commands/workflow.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/core.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli/display.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/cli.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/data_sync.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/builtin/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/detector.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/hardware.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/hardware_rules.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/probes.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/remote.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/templates.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/detect/workflow.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/discovery.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl/loader.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl/parser.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/context.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/decorators.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/docker_steps.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/exceptions.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/runner.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/dsl_python/steps.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/fleet.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/config_txt.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/data/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/data/hyperpixel.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/data/official.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/data/waveshare.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/fixes.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/panels.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/hardware/raspi_config.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/iac/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/iac/base.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/iac/docker_compose.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/iac/parsers/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/iac/parsers/compose.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/iac/registry.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/markpact/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/markpact/compiler.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/markpact/models.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/markpact/parser.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/observe.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/parse.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/patterns.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plan/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plan/planner.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/builtin/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/builtin/browser_reload.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/builtin/hardware_diagnostic.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/builtin/notify.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/builtin/process_control.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/plugins/builtin/systemd_reload.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/spec_loader.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/ssh.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/steps.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_audit.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_cli.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_data_sync.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_discovery.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_dsl.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_examples.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_executor.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_fleet.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_git_integration.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_iac.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_manifest.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_markpact_compiler.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_markpact_parser.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_models.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_observe.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_parse.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_patterns.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_planner_edge.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_plugins.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_probes.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_public_api.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_resume.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_spec_loader.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_ssh.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_templates.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_verify.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_version.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_version_changelog.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_version_cli.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_version_manifest.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_version_sources.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/tests/test_workflow.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/verify.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/bump.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/changelog.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/commits.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/diff.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/git_integration.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/git_transaction.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/manifest.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/__init__.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/base.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/json_.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/plain.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/regex.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/toml_.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/sources/yaml_.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version/transaction.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy/version.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy.egg-info/dependency_links.txt +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy.egg-info/entry_points.txt +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/redeploy.egg-info/top_level.txt +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/setup.cfg +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/tests/test_hardware_config.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/tests/test_hardware_rules.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/tests/test_iac.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/tests/test_parse.py +0 -0
- {redeploy-0.2.41 → redeploy-0.2.43}/tests/test_redeploy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redeploy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.43
|
|
4
4
|
Summary: Infrastructure migration toolkit: detect → plan → apply
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -12,6 +12,7 @@ Requires-Dist: loguru>=0.7
|
|
|
12
12
|
Requires-Dist: paramiko>=3.0
|
|
13
13
|
Requires-Dist: httpx>=0.25
|
|
14
14
|
Requires-Dist: rich>=13.0
|
|
15
|
+
Requires-Dist: jmespath>=1.0
|
|
15
16
|
Requires-Dist: goal>=2.1.0
|
|
16
17
|
Requires-Dist: costs>=0.1.20
|
|
17
18
|
Requires-Dist: pfix>=0.1.60
|
|
@@ -27,17 +28,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
|
|
|
27
28
|
|
|
28
29
|
## AI Cost Tracking
|
|
29
30
|
|
|
30
|
-
     
|
|
32
|
+
  
|
|
32
33
|
|
|
33
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
34
|
-
- 👤 **Human dev:** ~$
|
|
34
|
+
- 🤖 **LLM usage:** $7.5000 (64 commits)
|
|
35
|
+
- 👤 **Human dev:** ~$2099 (21.0h @ $100/h, 30min dedup)
|
|
35
36
|
|
|
36
37
|
Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
37
38
|
|
|
38
39
|
---
|
|
39
40
|
|
|
40
|
-
    
|
|
41
42
|
|
|
42
43
|
Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
|
|
43
44
|
|
|
@@ -152,6 +153,72 @@ redeploy device-rm root@10.0.0.5
|
|
|
152
153
|
|
|
153
154
|
Registry is stored at `~/.config/redeploy/devices.yaml` (chmod 600 — safe for SSH key paths).
|
|
154
155
|
|
|
156
|
+
## Declarative config workflow
|
|
157
|
+
|
|
158
|
+
redeploy supports a declarative configuration workflow for hardware settings — scan to YAML, edit locally, and apply to device:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# 1. Scan hardware state to YAML
|
|
162
|
+
redeploy hardware pi@192.168.188.109 > hardware.yaml
|
|
163
|
+
redeploy device-map pi@192.168.188.109 > device-map.yaml
|
|
164
|
+
|
|
165
|
+
# 2. Edit YAML locally (e.g., change display rotation)
|
|
166
|
+
# hardware.yaml:
|
|
167
|
+
# drm_outputs:
|
|
168
|
+
# - name: card0-DSI-2
|
|
169
|
+
# connector: DSI-2
|
|
170
|
+
# transform: '270' # ← edit this value
|
|
171
|
+
|
|
172
|
+
# 3. Apply config to remote device
|
|
173
|
+
redeploy hardware pi@192.168.188.109 --apply-config hardware.yaml
|
|
174
|
+
redeploy device-map pi@192.168.188.109 --apply-config device-map.yaml
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**What `--apply-config` does:**
|
|
178
|
+
- Applies display transforms via `wlr-randr` (Wayland compositor)
|
|
179
|
+
- Updates kanshi config (`~/.config/kanshi/config`) for persistent display rotation
|
|
180
|
+
- Sets backlight brightness and power state
|
|
181
|
+
- Supports both YAML and JSON config files
|
|
182
|
+
|
|
183
|
+
**Supported commands:**
|
|
184
|
+
- `redeploy hardware --apply-config FILE`
|
|
185
|
+
- `redeploy device-map --apply-config FILE`
|
|
186
|
+
- `redeploy blueprint show FILE --apply-config FILE`
|
|
187
|
+
|
|
188
|
+
## JMESPath query support
|
|
189
|
+
|
|
190
|
+
Extract specific values from YAML/JSON output using JMESPath query language (similar to XPath for XML):
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Simple path queries
|
|
194
|
+
redeploy hardware pi@192.168.188.109 --query "drm_outputs[0].transform"
|
|
195
|
+
redeploy hardware pi@192.168.188.109 --query "kernel"
|
|
196
|
+
redeploy device-map pi@192.168.188.109 --query "host"
|
|
197
|
+
|
|
198
|
+
# Filter queries
|
|
199
|
+
redeploy hardware pi@192.168.188.109 --query "backlights[?name==\`11-0045\`].brightness"
|
|
200
|
+
redeploy hardware pi@192.168.188.109 --query "drm_outputs[?connector==\`DSI-2\`].transform"
|
|
201
|
+
|
|
202
|
+
# From saved YAML files
|
|
203
|
+
redeploy blueprint show blueprint.yaml --query "hardware.drm_outputs[0].transform"
|
|
204
|
+
redeploy device-map --show device-map.yaml --query "tags"
|
|
205
|
+
|
|
206
|
+
# JSON output
|
|
207
|
+
redeploy hardware pi@192.168.188.109 --query "tags" --format json
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**JMESPath features:**
|
|
211
|
+
- **Simple paths:** `kernel`, `host`, `board`
|
|
212
|
+
- **Array indexing:** `drm_outputs[0].transform`
|
|
213
|
+
- **Filtering:** `[?name==\`11-0045\`]`
|
|
214
|
+
- **Projections:** `backlights[0].[name,brightness]`
|
|
215
|
+
- **Wildcards:** `drm_outputs[*].transform`
|
|
216
|
+
|
|
217
|
+
**Supported commands:**
|
|
218
|
+
- `redeploy hardware --query EXPR`
|
|
219
|
+
- `redeploy device-map --query EXPR`
|
|
220
|
+
- `redeploy blueprint show FILE --query EXPR`
|
|
221
|
+
|
|
155
222
|
## CLI reference
|
|
156
223
|
|
|
157
224
|
### `redeploy run SPEC [options]`
|
|
@@ -614,6 +681,90 @@ redeploy run examples/01-vps-version-bump/migration.yaml --plan-only
|
|
|
614
681
|
redeploy run examples/04-rpi-kiosk/migration.yaml --plan-only
|
|
615
682
|
```
|
|
616
683
|
|
|
684
|
+
## Hardware diagnostics commands
|
|
685
|
+
|
|
686
|
+
### `redeploy hardware HOST [options]`
|
|
687
|
+
|
|
688
|
+
Probe and diagnose hardware on a remote host (DSI display, DRM connectors, backlight, I2C, config.txt, Wayland compositor).
|
|
689
|
+
|
|
690
|
+
| Option | Description |
|
|
691
|
+
|--------|-------------|
|
|
692
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
693
|
+
| `--fix` | Print fix commands for all issues found |
|
|
694
|
+
| `--apply-fix COMPONENT` | Run fix for specific component via SSH |
|
|
695
|
+
| `--panel PANEL_ID` | Specify panel ID explicitly |
|
|
696
|
+
| `--list-panels` | List available panel definitions |
|
|
697
|
+
| `--set-transform TRANSFORM` | Set display rotation for DSI output (normal, 90, 180, 270, flipped, etc.) |
|
|
698
|
+
| `--apply-config FILE` | Apply display settings from YAML/JSON config file |
|
|
699
|
+
| `--query EXPR` | Extract specific values using JMESPath query |
|
|
700
|
+
| `--ssh-key PATH` | SSH private key path |
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
redeploy hardware pi@192.168.188.109
|
|
704
|
+
redeploy hardware pi@192.168.188.109 --fix
|
|
705
|
+
redeploy hardware pi@192.168.188.109 --set-transform 270
|
|
706
|
+
redeploy hardware pi@192.168.188.109 --apply-config hardware.yaml
|
|
707
|
+
redeploy hardware pi@192.168.188.109 --query "drm_outputs[0].transform"
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### `redeploy device-map HOST [options]`
|
|
711
|
+
|
|
712
|
+
Generate full device snapshot (hardware + infra + diagnostics).
|
|
713
|
+
|
|
714
|
+
| Option | Description |
|
|
715
|
+
|--------|-------------|
|
|
716
|
+
| `--name TEXT` | Human-friendly device label |
|
|
717
|
+
| `--tag TEXT` | Tag(s) to attach (repeatable) |
|
|
718
|
+
| `--save` | Persist map to `~/.config/redeploy/device-maps/` |
|
|
719
|
+
| `--out PATH` | Save to specific file |
|
|
720
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
721
|
+
| `--no-infra` | Skip infra probe (hardware only) |
|
|
722
|
+
| `--list` | List saved device maps |
|
|
723
|
+
| `--show PATH` | Load and display saved device-map file |
|
|
724
|
+
| `--diff PATH...` | Diff two saved device-map files |
|
|
725
|
+
| `--apply-config FILE` | Apply hardware/infra settings from YAML config file |
|
|
726
|
+
| `--query EXPR` | Extract specific values using JMESPath query |
|
|
727
|
+
| `--ssh-key PATH` | SSH private key path |
|
|
728
|
+
|
|
729
|
+
```bash
|
|
730
|
+
redeploy device-map pi@192.168.188.109 --save --name "kiosk-lab"
|
|
731
|
+
redeploy device-map --list
|
|
732
|
+
redeploy device-map --show ~/.config/redeploy/device-maps/pi_at_192.168.188.109.yaml
|
|
733
|
+
redeploy device-map pi@192.168.188.109 --apply-config device-map.yaml
|
|
734
|
+
redeploy device-map pi@192.168.188.109 --query "hardware.drm_outputs[0].transform"
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### `redeploy blueprint [command]`
|
|
738
|
+
|
|
739
|
+
Manage device blueprints (capture, show, list, twin, migrate).
|
|
740
|
+
|
|
741
|
+
#### `redeploy blueprint capture HOST [options]`
|
|
742
|
+
|
|
743
|
+
Capture device state as blueprint.
|
|
744
|
+
|
|
745
|
+
| Option | Description |
|
|
746
|
+
|--------|-------------|
|
|
747
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
748
|
+
| `--save` | Persist to `~/.config/redeploy/blueprints/` |
|
|
749
|
+
| `--out PATH` | Save to specific file |
|
|
750
|
+
| `--ssh-key PATH` | SSH private key path |
|
|
751
|
+
|
|
752
|
+
#### `redeploy blueprint show FILE [options]`
|
|
753
|
+
|
|
754
|
+
Display saved blueprint.
|
|
755
|
+
|
|
756
|
+
| Option | Description |
|
|
757
|
+
|--------|-------------|
|
|
758
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
759
|
+
| `--apply-config FILE` | Apply blueprint settings from YAML config file |
|
|
760
|
+
| `--query EXPR` | Extract specific values using JMESPath query |
|
|
761
|
+
|
|
762
|
+
```bash
|
|
763
|
+
redeploy blueprint capture pi@192.168.188.109 > blueprint.yaml
|
|
764
|
+
redeploy blueprint show blueprint.yaml --apply-config blueprint.yaml
|
|
765
|
+
redeploy blueprint show blueprint.yaml --query "hardware.drm_outputs[0].transform"
|
|
766
|
+
```
|
|
767
|
+
|
|
617
768
|
## License
|
|
618
769
|
|
|
619
770
|
Licensed under Apache-2.0.
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
     
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $7.5000 (64 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$2099 (21.0h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
12
|
Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
    
|
|
17
17
|
|
|
18
18
|
Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
|
|
19
19
|
|
|
@@ -128,6 +128,72 @@ redeploy device-rm root@10.0.0.5
|
|
|
128
128
|
|
|
129
129
|
Registry is stored at `~/.config/redeploy/devices.yaml` (chmod 600 — safe for SSH key paths).
|
|
130
130
|
|
|
131
|
+
## Declarative config workflow
|
|
132
|
+
|
|
133
|
+
redeploy supports a declarative configuration workflow for hardware settings — scan to YAML, edit locally, and apply to device:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# 1. Scan hardware state to YAML
|
|
137
|
+
redeploy hardware pi@192.168.188.109 > hardware.yaml
|
|
138
|
+
redeploy device-map pi@192.168.188.109 > device-map.yaml
|
|
139
|
+
|
|
140
|
+
# 2. Edit YAML locally (e.g., change display rotation)
|
|
141
|
+
# hardware.yaml:
|
|
142
|
+
# drm_outputs:
|
|
143
|
+
# - name: card0-DSI-2
|
|
144
|
+
# connector: DSI-2
|
|
145
|
+
# transform: '270' # ← edit this value
|
|
146
|
+
|
|
147
|
+
# 3. Apply config to remote device
|
|
148
|
+
redeploy hardware pi@192.168.188.109 --apply-config hardware.yaml
|
|
149
|
+
redeploy device-map pi@192.168.188.109 --apply-config device-map.yaml
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**What `--apply-config` does:**
|
|
153
|
+
- Applies display transforms via `wlr-randr` (Wayland compositor)
|
|
154
|
+
- Updates kanshi config (`~/.config/kanshi/config`) for persistent display rotation
|
|
155
|
+
- Sets backlight brightness and power state
|
|
156
|
+
- Supports both YAML and JSON config files
|
|
157
|
+
|
|
158
|
+
**Supported commands:**
|
|
159
|
+
- `redeploy hardware --apply-config FILE`
|
|
160
|
+
- `redeploy device-map --apply-config FILE`
|
|
161
|
+
- `redeploy blueprint show FILE --apply-config FILE`
|
|
162
|
+
|
|
163
|
+
## JMESPath query support
|
|
164
|
+
|
|
165
|
+
Extract specific values from YAML/JSON output using JMESPath query language (similar to XPath for XML):
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Simple path queries
|
|
169
|
+
redeploy hardware pi@192.168.188.109 --query "drm_outputs[0].transform"
|
|
170
|
+
redeploy hardware pi@192.168.188.109 --query "kernel"
|
|
171
|
+
redeploy device-map pi@192.168.188.109 --query "host"
|
|
172
|
+
|
|
173
|
+
# Filter queries
|
|
174
|
+
redeploy hardware pi@192.168.188.109 --query "backlights[?name==\`11-0045\`].brightness"
|
|
175
|
+
redeploy hardware pi@192.168.188.109 --query "drm_outputs[?connector==\`DSI-2\`].transform"
|
|
176
|
+
|
|
177
|
+
# From saved YAML files
|
|
178
|
+
redeploy blueprint show blueprint.yaml --query "hardware.drm_outputs[0].transform"
|
|
179
|
+
redeploy device-map --show device-map.yaml --query "tags"
|
|
180
|
+
|
|
181
|
+
# JSON output
|
|
182
|
+
redeploy hardware pi@192.168.188.109 --query "tags" --format json
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**JMESPath features:**
|
|
186
|
+
- **Simple paths:** `kernel`, `host`, `board`
|
|
187
|
+
- **Array indexing:** `drm_outputs[0].transform`
|
|
188
|
+
- **Filtering:** `[?name==\`11-0045\`]`
|
|
189
|
+
- **Projections:** `backlights[0].[name,brightness]`
|
|
190
|
+
- **Wildcards:** `drm_outputs[*].transform`
|
|
191
|
+
|
|
192
|
+
**Supported commands:**
|
|
193
|
+
- `redeploy hardware --query EXPR`
|
|
194
|
+
- `redeploy device-map --query EXPR`
|
|
195
|
+
- `redeploy blueprint show FILE --query EXPR`
|
|
196
|
+
|
|
131
197
|
## CLI reference
|
|
132
198
|
|
|
133
199
|
### `redeploy run SPEC [options]`
|
|
@@ -590,6 +656,90 @@ redeploy run examples/01-vps-version-bump/migration.yaml --plan-only
|
|
|
590
656
|
redeploy run examples/04-rpi-kiosk/migration.yaml --plan-only
|
|
591
657
|
```
|
|
592
658
|
|
|
659
|
+
## Hardware diagnostics commands
|
|
660
|
+
|
|
661
|
+
### `redeploy hardware HOST [options]`
|
|
662
|
+
|
|
663
|
+
Probe and diagnose hardware on a remote host (DSI display, DRM connectors, backlight, I2C, config.txt, Wayland compositor).
|
|
664
|
+
|
|
665
|
+
| Option | Description |
|
|
666
|
+
|--------|-------------|
|
|
667
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
668
|
+
| `--fix` | Print fix commands for all issues found |
|
|
669
|
+
| `--apply-fix COMPONENT` | Run fix for specific component via SSH |
|
|
670
|
+
| `--panel PANEL_ID` | Specify panel ID explicitly |
|
|
671
|
+
| `--list-panels` | List available panel definitions |
|
|
672
|
+
| `--set-transform TRANSFORM` | Set display rotation for DSI output (normal, 90, 180, 270, flipped, etc.) |
|
|
673
|
+
| `--apply-config FILE` | Apply display settings from YAML/JSON config file |
|
|
674
|
+
| `--query EXPR` | Extract specific values using JMESPath query |
|
|
675
|
+
| `--ssh-key PATH` | SSH private key path |
|
|
676
|
+
|
|
677
|
+
```bash
|
|
678
|
+
redeploy hardware pi@192.168.188.109
|
|
679
|
+
redeploy hardware pi@192.168.188.109 --fix
|
|
680
|
+
redeploy hardware pi@192.168.188.109 --set-transform 270
|
|
681
|
+
redeploy hardware pi@192.168.188.109 --apply-config hardware.yaml
|
|
682
|
+
redeploy hardware pi@192.168.188.109 --query "drm_outputs[0].transform"
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### `redeploy device-map HOST [options]`
|
|
686
|
+
|
|
687
|
+
Generate full device snapshot (hardware + infra + diagnostics).
|
|
688
|
+
|
|
689
|
+
| Option | Description |
|
|
690
|
+
|--------|-------------|
|
|
691
|
+
| `--name TEXT` | Human-friendly device label |
|
|
692
|
+
| `--tag TEXT` | Tag(s) to attach (repeatable) |
|
|
693
|
+
| `--save` | Persist map to `~/.config/redeploy/device-maps/` |
|
|
694
|
+
| `--out PATH` | Save to specific file |
|
|
695
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
696
|
+
| `--no-infra` | Skip infra probe (hardware only) |
|
|
697
|
+
| `--list` | List saved device maps |
|
|
698
|
+
| `--show PATH` | Load and display saved device-map file |
|
|
699
|
+
| `--diff PATH...` | Diff two saved device-map files |
|
|
700
|
+
| `--apply-config FILE` | Apply hardware/infra settings from YAML config file |
|
|
701
|
+
| `--query EXPR` | Extract specific values using JMESPath query |
|
|
702
|
+
| `--ssh-key PATH` | SSH private key path |
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
redeploy device-map pi@192.168.188.109 --save --name "kiosk-lab"
|
|
706
|
+
redeploy device-map --list
|
|
707
|
+
redeploy device-map --show ~/.config/redeploy/device-maps/pi_at_192.168.188.109.yaml
|
|
708
|
+
redeploy device-map pi@192.168.188.109 --apply-config device-map.yaml
|
|
709
|
+
redeploy device-map pi@192.168.188.109 --query "hardware.drm_outputs[0].transform"
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### `redeploy blueprint [command]`
|
|
713
|
+
|
|
714
|
+
Manage device blueprints (capture, show, list, twin, migrate).
|
|
715
|
+
|
|
716
|
+
#### `redeploy blueprint capture HOST [options]`
|
|
717
|
+
|
|
718
|
+
Capture device state as blueprint.
|
|
719
|
+
|
|
720
|
+
| Option | Description |
|
|
721
|
+
|--------|-------------|
|
|
722
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
723
|
+
| `--save` | Persist to `~/.config/redeploy/blueprints/` |
|
|
724
|
+
| `--out PATH` | Save to specific file |
|
|
725
|
+
| `--ssh-key PATH` | SSH private key path |
|
|
726
|
+
|
|
727
|
+
#### `redeploy blueprint show FILE [options]`
|
|
728
|
+
|
|
729
|
+
Display saved blueprint.
|
|
730
|
+
|
|
731
|
+
| Option | Description |
|
|
732
|
+
|--------|-------------|
|
|
733
|
+
| `--format [yaml|json]` | Output format (default: yaml) |
|
|
734
|
+
| `--apply-config FILE` | Apply blueprint settings from YAML config file |
|
|
735
|
+
| `--query EXPR` | Extract specific values using JMESPath query |
|
|
736
|
+
|
|
737
|
+
```bash
|
|
738
|
+
redeploy blueprint capture pi@192.168.188.109 > blueprint.yaml
|
|
739
|
+
redeploy blueprint show blueprint.yaml --apply-config blueprint.yaml
|
|
740
|
+
redeploy blueprint show blueprint.yaml --query "hardware.drm_outputs[0].transform"
|
|
741
|
+
```
|
|
742
|
+
|
|
593
743
|
## License
|
|
594
744
|
|
|
595
745
|
Licensed under Apache-2.0.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "redeploy"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.43"
|
|
8
8
|
description = "Infrastructure migration toolkit: detect → plan → apply"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -17,6 +17,7 @@ dependencies = [
|
|
|
17
17
|
"paramiko>=3.0",
|
|
18
18
|
"httpx>=0.25",
|
|
19
19
|
"rich>=13.0",
|
|
20
|
+
"jmespath>=1.0",
|
|
20
21
|
"goal>=2.1.0",
|
|
21
22
|
"costs>=0.1.20",
|
|
22
23
|
"pfix>=0.1.60",
|
|
@@ -7,7 +7,7 @@ Public API (stable from 0.2.0, semver guaranteed)::
|
|
|
7
7
|
|
|
8
8
|
Everything not listed in ``__all__`` is internal and may change without notice.
|
|
9
9
|
"""
|
|
10
|
-
__version__ = "0.2.
|
|
10
|
+
__version__ = "0.2.43"
|
|
11
11
|
|
|
12
12
|
# ── Core models ───────────────────────────────────────────────────────────────
|
|
13
13
|
from .models import ( # noqa: F401
|
|
@@ -16,6 +16,7 @@ from .handlers import (
|
|
|
16
16
|
run_docker_health_wait, run_container_log_tail, run_http_check,
|
|
17
17
|
run_version_check, run_plugin, run_wait, run_inline_script,
|
|
18
18
|
run_ensure_config_line, run_raspi_config,
|
|
19
|
+
run_ensure_kanshi_profile, run_ensure_autostart_entry, run_ensure_browser_kiosk_script,
|
|
19
20
|
)
|
|
20
21
|
from .progress import ProgressEmitter
|
|
21
22
|
from .rollback import rollback_steps
|
|
@@ -230,6 +231,9 @@ class Executor:
|
|
|
230
231
|
StepAction.INLINE_SCRIPT: lambda s: run_inline_script(s, self.probe, self.plan),
|
|
231
232
|
StepAction.ENSURE_CONFIG_LINE: lambda s: run_ensure_config_line(s, self.probe),
|
|
232
233
|
StepAction.RASPI_CONFIG: lambda s: run_raspi_config(s, self.probe),
|
|
234
|
+
StepAction.ENSURE_KANSHI_PROFILE: lambda s: run_ensure_kanshi_profile(s, self.probe),
|
|
235
|
+
StepAction.ENSURE_AUTOSTART_ENTRY: lambda s: run_ensure_autostart_entry(s, self.probe),
|
|
236
|
+
StepAction.ENSURE_BROWSER_KIOSK_SCRIPT: lambda s: run_ensure_browser_kiosk_script(s, self.probe),
|
|
233
237
|
}
|
|
234
238
|
|
|
235
239
|
handler = dispatch.get(step.action)
|
|
@@ -398,3 +398,137 @@ def run_raspi_config(step: MigrationStep, probe: "RemoteProbe") -> None:
|
|
|
398
398
|
|
|
399
399
|
step.status = StepStatus.DONE
|
|
400
400
|
step.result = f"applied: {cmd}"
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ── kiosk handlers ────────────────────────────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
def run_ensure_kanshi_profile(step: MigrationStep, probe: "RemoteProbe") -> None:
|
|
406
|
+
"""Idempotently write or replace a named kanshi output profile.
|
|
407
|
+
|
|
408
|
+
step.command — the profile block to write (rendered kanshi syntax)
|
|
409
|
+
step.config_file — kanshi config path (default: ~/.config/kanshi/config)
|
|
410
|
+
"""
|
|
411
|
+
from ..hardware.kiosk.output_profiles import OutputProfile
|
|
412
|
+
|
|
413
|
+
profile_block = step.command
|
|
414
|
+
if not profile_block:
|
|
415
|
+
raise StepError(step, "ensure_kanshi_profile requires step.command with profile block")
|
|
416
|
+
|
|
417
|
+
config_path = step.config_file or "~/.config/kanshi/config"
|
|
418
|
+
mkdir_cmd = f"mkdir -p $(dirname {config_path})"
|
|
419
|
+
probe.run(mkdir_cmd, timeout=10)
|
|
420
|
+
|
|
421
|
+
r = probe.run(f"cat {config_path} 2>/dev/null || echo ''", timeout=10)
|
|
422
|
+
existing = r.out if r.ok else ""
|
|
423
|
+
|
|
424
|
+
# Extract profile name from block (first line: "profile <name> {")
|
|
425
|
+
first_line = profile_block.strip().splitlines()[0]
|
|
426
|
+
import re
|
|
427
|
+
m = re.match(r"profile\s+(\S+)\s*\{", first_line)
|
|
428
|
+
if not m:
|
|
429
|
+
raise StepError(step, f"Cannot parse profile name from block: {first_line!r}")
|
|
430
|
+
profile_name = m.group(1)
|
|
431
|
+
|
|
432
|
+
# Replace existing profile block or append
|
|
433
|
+
pattern = re.compile(
|
|
434
|
+
rf"^profile\s+{re.escape(profile_name)}\s*\{{[^}}]*\}}", re.MULTILINE | re.DOTALL
|
|
435
|
+
)
|
|
436
|
+
if pattern.search(existing):
|
|
437
|
+
if profile_block.strip() in existing:
|
|
438
|
+
step.status = StepStatus.DONE
|
|
439
|
+
step.result = f"no-op: profile '{profile_name}' already correct"
|
|
440
|
+
return
|
|
441
|
+
new_content = pattern.sub(profile_block.strip(), existing)
|
|
442
|
+
changed = True
|
|
443
|
+
else:
|
|
444
|
+
sep = "\n" if existing and not existing.endswith("\n") else ""
|
|
445
|
+
new_content = existing + sep + profile_block.strip() + "\n"
|
|
446
|
+
changed = True
|
|
447
|
+
|
|
448
|
+
encoded = base64.b64encode(new_content.encode()).decode()
|
|
449
|
+
tmp = f"/tmp/redeploy-kanshi-{step.id}.conf"
|
|
450
|
+
write_r = probe.run(
|
|
451
|
+
f"echo '{encoded}' | base64 -d | tee {tmp} > /dev/null && mv {tmp} {config_path}",
|
|
452
|
+
timeout=15,
|
|
453
|
+
)
|
|
454
|
+
if not write_r.ok:
|
|
455
|
+
raise StepError(step, f"Cannot write {config_path}: {write_r.stderr[:200]}")
|
|
456
|
+
|
|
457
|
+
# Reload kanshi if running
|
|
458
|
+
probe.run("pkill -SIGUSR1 kanshi 2>/dev/null || true", timeout=5)
|
|
459
|
+
|
|
460
|
+
step.status = StepStatus.DONE
|
|
461
|
+
step.result = f"profile '{profile_name}' written to {config_path}"
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def run_ensure_autostart_entry(step: MigrationStep, probe: "RemoteProbe") -> None:
|
|
465
|
+
"""Idempotently add or replace a keyed entry in a compositor autostart file.
|
|
466
|
+
|
|
467
|
+
step.config_file — path to autostart file
|
|
468
|
+
step.config_line — the line to write (the entry body)
|
|
469
|
+
step.config_section — used as the entry key for idempotent marker
|
|
470
|
+
"""
|
|
471
|
+
from ..hardware.kiosk.autostart import AutostartEntry, ensure_autostart_entry
|
|
472
|
+
|
|
473
|
+
if not step.config_file or not step.config_line:
|
|
474
|
+
raise StepError(step, "ensure_autostart_entry requires config_file and config_line")
|
|
475
|
+
|
|
476
|
+
key = step.config_section or "redeploy"
|
|
477
|
+
entry = AutostartEntry(key=key, line=step.config_line)
|
|
478
|
+
|
|
479
|
+
r = probe.run(f"cat {step.config_file} 2>/dev/null || echo ''", timeout=10)
|
|
480
|
+
existing = r.out if r.ok else ""
|
|
481
|
+
|
|
482
|
+
new_content, changed = ensure_autostart_entry(existing, entry)
|
|
483
|
+
if not changed:
|
|
484
|
+
step.status = StepStatus.DONE
|
|
485
|
+
step.result = f"no-op: entry [{key}] already correct"
|
|
486
|
+
return
|
|
487
|
+
|
|
488
|
+
mkdir_cmd = f"mkdir -p $(dirname {step.config_file})"
|
|
489
|
+
probe.run(mkdir_cmd, timeout=10)
|
|
490
|
+
|
|
491
|
+
encoded = base64.b64encode(new_content.encode()).decode()
|
|
492
|
+
tmp = f"/tmp/redeploy-autostart-{step.id}.txt"
|
|
493
|
+
write_r = probe.run(
|
|
494
|
+
f"echo '{encoded}' | base64 -d | tee {tmp} > /dev/null && mv {tmp} {step.config_file}",
|
|
495
|
+
timeout=15,
|
|
496
|
+
)
|
|
497
|
+
if not write_r.ok:
|
|
498
|
+
raise StepError(step, f"Cannot write {step.config_file}: {write_r.stderr[:200]}")
|
|
499
|
+
|
|
500
|
+
step.status = StepStatus.DONE
|
|
501
|
+
step.result = f"entry [{key}] written to {step.config_file}"
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def run_ensure_browser_kiosk_script(step: MigrationStep, probe: "RemoteProbe") -> None:
|
|
505
|
+
"""Write a kiosk-launch.sh script to the remote device.
|
|
506
|
+
|
|
507
|
+
step.command — the full script content to write
|
|
508
|
+
step.dst — destination path (default: ~/kiosk-launch.sh)
|
|
509
|
+
"""
|
|
510
|
+
script_content = step.command
|
|
511
|
+
if not script_content:
|
|
512
|
+
raise StepError(step, "ensure_browser_kiosk_script requires step.command with script body")
|
|
513
|
+
|
|
514
|
+
dst = step.dst or "~/kiosk-launch.sh"
|
|
515
|
+
|
|
516
|
+
r = probe.run(f"cat {dst} 2>/dev/null || echo ''", timeout=10)
|
|
517
|
+
existing = r.out if r.ok else ""
|
|
518
|
+
if existing.strip() == script_content.strip():
|
|
519
|
+
step.status = StepStatus.DONE
|
|
520
|
+
step.result = f"no-op: {dst} already correct"
|
|
521
|
+
return
|
|
522
|
+
|
|
523
|
+
encoded = base64.b64encode(script_content.encode()).decode()
|
|
524
|
+
tmp = f"/tmp/redeploy-kiosk-{step.id}.sh"
|
|
525
|
+
write_r = probe.run(
|
|
526
|
+
f"echo '{encoded}' | base64 -d | tee {tmp} > /dev/null"
|
|
527
|
+
f" && chmod +x {tmp} && mv {tmp} {dst}",
|
|
528
|
+
timeout=15,
|
|
529
|
+
)
|
|
530
|
+
if not write_r.ok:
|
|
531
|
+
raise StepError(step, f"Cannot write {dst}: {write_r.stderr[:200]}")
|
|
532
|
+
|
|
533
|
+
step.status = StepStatus.DONE
|
|
534
|
+
step.result = f"kiosk script written to {dst}"
|