redeploy 0.2.17__tar.gz → 0.2.19__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.17 → redeploy-0.2.19}/PKG-INFO +108 -6
- {redeploy-0.2.17 → redeploy-0.2.19}/README.md +107 -5
- {redeploy-0.2.17 → redeploy-0.2.19}/pyproject.toml +1 -1
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/__init__.py +1 -1
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/apply/executor.py +67 -5
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/cli.py +765 -290
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/markpact/__init__.py +11 -1
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/markpact/compiler.py +7 -2
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/markpact/models.py +3 -1
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/markpact/parser.py +111 -4
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/models.py +2 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_executor.py +56 -0
- redeploy-0.2.19/redeploy/tests/test_markpact_parser.py +93 -0
- redeploy-0.2.19/redeploy/tests/test_resume.py +271 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy.egg-info/PKG-INFO +108 -6
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy.egg-info/SOURCES.txt +1 -1
- redeploy-0.2.17/redeploy/checkpoint.py +0 -234
- redeploy-0.2.17/redeploy/tests/test_markpact_parser.py +0 -41
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/apply/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/apply/state.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/audit.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/data_sync.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/detect/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/detect/detector.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/detect/probes.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/detect/remote.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/detect/templates.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/detect/workflow.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/discovery.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl/loader.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl/parser.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/context.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/decorators.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/docker_steps.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/exceptions.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/runner.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/dsl_python/steps.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/fleet.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/iac/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/iac/base.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/iac/docker_compose.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/iac/parsers/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/iac/parsers/compose.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/iac/registry.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/observe.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/parse.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/patterns.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plan/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plan/planner.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plugins/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plugins/builtin/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plugins/builtin/browser_reload.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plugins/builtin/notify.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/plugins/builtin/systemd_reload.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/spec_loader.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/ssh.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/steps.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_audit.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_cli.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_data_sync.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_discovery.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_dsl.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_examples.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_fleet.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_git_integration.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_iac.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_manifest.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_markpact_compiler.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_models.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_observe.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_parse.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_patterns.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_planner_edge.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_plugins.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_probes.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_public_api.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_spec_loader.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_ssh.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_templates.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_verify.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_version.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_version_changelog.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_version_cli.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_version_manifest.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_version_sources.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/tests/test_workflow.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/verify.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/bump.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/changelog.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/commits.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/diff.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/git_integration.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/git_transaction.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/manifest.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/__init__.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/base.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/json_.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/plain.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/regex.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/toml_.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/sources/yaml_.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version/transaction.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy/version.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy.egg-info/dependency_links.txt +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy.egg-info/entry_points.txt +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy.egg-info/requires.txt +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/redeploy.egg-info/top_level.txt +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/setup.cfg +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/tests/test_iac.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/tests/test_parse.py +0 -0
- {redeploy-0.2.17 → redeploy-0.2.19}/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.19
|
|
4
4
|
Summary: Infrastructure migration toolkit: detect → plan → apply
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -27,17 +27,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
|
|
|
27
27
|
|
|
28
28
|
## AI Cost Tracking
|
|
29
29
|
|
|
30
|
-
    
|
|
31
|
+
  
|
|
32
32
|
|
|
33
|
-
- 🤖 **LLM usage:** $5.
|
|
34
|
-
- 👤 **Human dev:** ~$
|
|
33
|
+
- 🤖 **LLM usage:** $5.7000 (38 commits)
|
|
34
|
+
- 👤 **Human dev:** ~$1297 (13.0h @ $100/h, 30min dedup)
|
|
35
35
|
|
|
36
36
|
Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
-
    
|
|
41
41
|
|
|
42
42
|
Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
|
|
43
43
|
|
|
@@ -428,6 +428,108 @@ extra_steps:
|
|
|
428
428
|
|
|
429
429
|
The script is base64-encoded and executed via SSH with automatic temp file cleanup. Use `command` field for multiline script content (YAML `|` preserves newlines).
|
|
430
430
|
|
|
431
|
+
### Script References (`command_ref`)
|
|
432
|
+
|
|
433
|
+
Instead of duplicating scripts in YAML, reference a script defined in a markdown codeblock:
|
|
434
|
+
|
|
435
|
+
```yaml
|
|
436
|
+
extra_steps:
|
|
437
|
+
- id: configure_kiosk
|
|
438
|
+
action: inline_script
|
|
439
|
+
description: "Execute kiosk script from markdown"
|
|
440
|
+
command_ref: "#kiosk-browser-configuration-script"
|
|
441
|
+
risk: medium
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
In your migration markdown file, define the script in a section:
|
|
445
|
+
|
|
446
|
+
```markdown
|
|
447
|
+
## Kiosk Browser Configuration Script
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
#!/bin/bash
|
|
451
|
+
# Auto-detect browser...
|
|
452
|
+
if command -v chromium-browser >/dev/null 2>&1; then
|
|
453
|
+
chromium-browser --kiosk http://localhost:8100
|
|
454
|
+
fi
|
|
455
|
+
```
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Benefits:**
|
|
459
|
+
- Single source of truth — script lives in one place (markdown codeblock)
|
|
460
|
+
- No duplication between markdown documentation and YAML
|
|
461
|
+
- Easy to read and maintain
|
|
462
|
+
- Changes to the codeblock automatically apply to the deployment
|
|
463
|
+
|
|
464
|
+
**Reference formats:**
|
|
465
|
+
- `"#section-id"` — script from section in current spec file
|
|
466
|
+
- `"./file.md#section-id"` — script from section in specific file
|
|
467
|
+
|
|
468
|
+
The section ID is derived from the heading: spaces become hyphens, lowercase.
|
|
469
|
+
Example: `## Kiosk Browser Configuration Script` → `#kiosk-browser-configuration-script`
|
|
470
|
+
|
|
471
|
+
### Execute Script by Reference (`redeploy exec`)
|
|
472
|
+
|
|
473
|
+
Run a single script from markdown without running the full migration:
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
# Execute script from codeblock on remote host
|
|
477
|
+
redeploy exec '#kiosk-browser-configuration-script' \
|
|
478
|
+
--host pi@192.168.188.108 \
|
|
479
|
+
--file migration.podman-rpi5-resume.md
|
|
480
|
+
|
|
481
|
+
# With file in reference
|
|
482
|
+
redeploy exec './migration.md#install-deps' --host root@server.com
|
|
483
|
+
|
|
484
|
+
# Using markpact:ref (more explicit)
|
|
485
|
+
redeploy exec 'kiosk-script-id' --host pi@192.168.188.108 --file migration.md
|
|
486
|
+
|
|
487
|
+
# Dry-run to preview script
|
|
488
|
+
redeploy exec '#backup-script' --host pi@192.168.188.108 --file ops.md --dry-run
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
This is useful for:
|
|
492
|
+
- One-off operations defined in markdown docs
|
|
493
|
+
- Testing individual scripts before full migration
|
|
494
|
+
- Running maintenance tasks
|
|
495
|
+
|
|
496
|
+
### Execute Multiple Scripts (`redeploy exec-multi`)
|
|
497
|
+
|
|
498
|
+
Test multiple scripts at once:
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
# Execute multiple scripts by ref
|
|
502
|
+
redeploy exec-multi 'kiosk-script,install-deps,cleanup' \
|
|
503
|
+
--host pi@192.168.188.108 \
|
|
504
|
+
--file migration.md
|
|
505
|
+
|
|
506
|
+
# Mix of markpact:ref and section headings
|
|
507
|
+
redeploy exec-multi 'script1,#section2,script3' \
|
|
508
|
+
--host root@server.com \
|
|
509
|
+
--file deploy.md \
|
|
510
|
+
--dry-run
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Marking Codeblocks with `markpact:ref`
|
|
514
|
+
|
|
515
|
+
For more explicit script identification, use `markpact:ref <id>` in codeblock:
|
|
516
|
+
|
|
517
|
+
```markdown
|
|
518
|
+
```bash markpact:ref kiosk-browser-configuration-script
|
|
519
|
+
#!/bin/bash
|
|
520
|
+
# Auto-detect browser...
|
|
521
|
+
if command -v chromium-browser >/dev/null 2>&1; then
|
|
522
|
+
chromium-browser --kiosk http://localhost:8100
|
|
523
|
+
fi
|
|
524
|
+
```
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Benefits of `markpact:ref`:
|
|
528
|
+
- Explicit ID assignment (not derived from heading)
|
|
529
|
+
- Multiple scripts per section
|
|
530
|
+
- Can reference by simple ID instead of full heading
|
|
531
|
+
- Self-documenting in markdown
|
|
532
|
+
|
|
431
533
|
|
|
432
534
|
|
|
433
535
|
Place in project root — `redeploy run` (no args) uses it automatically.
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $5.
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $5.7000 (38 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$1297 (13.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
|
|
|
@@ -404,6 +404,108 @@ extra_steps:
|
|
|
404
404
|
|
|
405
405
|
The script is base64-encoded and executed via SSH with automatic temp file cleanup. Use `command` field for multiline script content (YAML `|` preserves newlines).
|
|
406
406
|
|
|
407
|
+
### Script References (`command_ref`)
|
|
408
|
+
|
|
409
|
+
Instead of duplicating scripts in YAML, reference a script defined in a markdown codeblock:
|
|
410
|
+
|
|
411
|
+
```yaml
|
|
412
|
+
extra_steps:
|
|
413
|
+
- id: configure_kiosk
|
|
414
|
+
action: inline_script
|
|
415
|
+
description: "Execute kiosk script from markdown"
|
|
416
|
+
command_ref: "#kiosk-browser-configuration-script"
|
|
417
|
+
risk: medium
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
In your migration markdown file, define the script in a section:
|
|
421
|
+
|
|
422
|
+
```markdown
|
|
423
|
+
## Kiosk Browser Configuration Script
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
#!/bin/bash
|
|
427
|
+
# Auto-detect browser...
|
|
428
|
+
if command -v chromium-browser >/dev/null 2>&1; then
|
|
429
|
+
chromium-browser --kiosk http://localhost:8100
|
|
430
|
+
fi
|
|
431
|
+
```
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Benefits:**
|
|
435
|
+
- Single source of truth — script lives in one place (markdown codeblock)
|
|
436
|
+
- No duplication between markdown documentation and YAML
|
|
437
|
+
- Easy to read and maintain
|
|
438
|
+
- Changes to the codeblock automatically apply to the deployment
|
|
439
|
+
|
|
440
|
+
**Reference formats:**
|
|
441
|
+
- `"#section-id"` — script from section in current spec file
|
|
442
|
+
- `"./file.md#section-id"` — script from section in specific file
|
|
443
|
+
|
|
444
|
+
The section ID is derived from the heading: spaces become hyphens, lowercase.
|
|
445
|
+
Example: `## Kiosk Browser Configuration Script` → `#kiosk-browser-configuration-script`
|
|
446
|
+
|
|
447
|
+
### Execute Script by Reference (`redeploy exec`)
|
|
448
|
+
|
|
449
|
+
Run a single script from markdown without running the full migration:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
# Execute script from codeblock on remote host
|
|
453
|
+
redeploy exec '#kiosk-browser-configuration-script' \
|
|
454
|
+
--host pi@192.168.188.108 \
|
|
455
|
+
--file migration.podman-rpi5-resume.md
|
|
456
|
+
|
|
457
|
+
# With file in reference
|
|
458
|
+
redeploy exec './migration.md#install-deps' --host root@server.com
|
|
459
|
+
|
|
460
|
+
# Using markpact:ref (more explicit)
|
|
461
|
+
redeploy exec 'kiosk-script-id' --host pi@192.168.188.108 --file migration.md
|
|
462
|
+
|
|
463
|
+
# Dry-run to preview script
|
|
464
|
+
redeploy exec '#backup-script' --host pi@192.168.188.108 --file ops.md --dry-run
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
This is useful for:
|
|
468
|
+
- One-off operations defined in markdown docs
|
|
469
|
+
- Testing individual scripts before full migration
|
|
470
|
+
- Running maintenance tasks
|
|
471
|
+
|
|
472
|
+
### Execute Multiple Scripts (`redeploy exec-multi`)
|
|
473
|
+
|
|
474
|
+
Test multiple scripts at once:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
# Execute multiple scripts by ref
|
|
478
|
+
redeploy exec-multi 'kiosk-script,install-deps,cleanup' \
|
|
479
|
+
--host pi@192.168.188.108 \
|
|
480
|
+
--file migration.md
|
|
481
|
+
|
|
482
|
+
# Mix of markpact:ref and section headings
|
|
483
|
+
redeploy exec-multi 'script1,#section2,script3' \
|
|
484
|
+
--host root@server.com \
|
|
485
|
+
--file deploy.md \
|
|
486
|
+
--dry-run
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Marking Codeblocks with `markpact:ref`
|
|
490
|
+
|
|
491
|
+
For more explicit script identification, use `markpact:ref <id>` in codeblock:
|
|
492
|
+
|
|
493
|
+
```markdown
|
|
494
|
+
```bash markpact:ref kiosk-browser-configuration-script
|
|
495
|
+
#!/bin/bash
|
|
496
|
+
# Auto-detect browser...
|
|
497
|
+
if command -v chromium-browser >/dev/null 2>&1; then
|
|
498
|
+
chromium-browser --kiosk http://localhost:8100
|
|
499
|
+
fi
|
|
500
|
+
```
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Benefits of `markpact:ref`:
|
|
504
|
+
- Explicit ID assignment (not derived from heading)
|
|
505
|
+
- Multiple scripts per section
|
|
506
|
+
- Can reference by simple ID instead of full heading
|
|
507
|
+
- Self-documenting in markdown
|
|
508
|
+
|
|
407
509
|
|
|
408
510
|
|
|
409
511
|
Place in project root — `redeploy run` (no args) uses it automatically.
|
|
@@ -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.19"
|
|
11
11
|
|
|
12
12
|
# ── Core models ───────────────────────────────────────────────────────────────
|
|
13
13
|
from .models import ( # noqa: F401
|
|
@@ -143,10 +143,14 @@ class Executor:
|
|
|
143
143
|
self._audit_path = audit_path
|
|
144
144
|
self._t0: float = 0.0
|
|
145
145
|
|
|
146
|
+
# ── spec path for command_ref resolution ─────────────────────────────
|
|
147
|
+
if spec_path and not plan.spec_path:
|
|
148
|
+
plan.spec_path = spec_path
|
|
149
|
+
|
|
146
150
|
# ── resume / checkpoint ──────────────────────────────────────────────
|
|
147
151
|
self._resume = resume
|
|
148
152
|
self._from_step = from_step
|
|
149
|
-
spec_id = spec_path or plan.target_file or plan.infra_file or plan.app
|
|
153
|
+
spec_id = spec_path or plan.spec_path or plan.target_file or plan.infra_file or plan.app
|
|
150
154
|
if state_path is None and not dry_run:
|
|
151
155
|
state_path = default_state_path(spec_id, plan.host)
|
|
152
156
|
self._state_path: Optional[Path] = (
|
|
@@ -292,6 +296,7 @@ class Executor:
|
|
|
292
296
|
StepAction.DOCKER_BUILD: self._run_docker_build,
|
|
293
297
|
StepAction.DOCKER_HEALTH_WAIT: self._run_docker_health_wait,
|
|
294
298
|
StepAction.CONTAINER_LOG_TAIL: self._run_container_log_tail,
|
|
299
|
+
StepAction.PODMAN_BUILD: self._run_podman_build,
|
|
295
300
|
StepAction.SSH_CMD: self._run_ssh,
|
|
296
301
|
StepAction.SCP: self._run_scp,
|
|
297
302
|
StepAction.RSYNC: self._run_rsync,
|
|
@@ -399,6 +404,54 @@ class Executor:
|
|
|
399
404
|
logger.debug(f" build completed in {elapsed + poll_interval}s")
|
|
400
405
|
step.status = StepStatus.DONE
|
|
401
406
|
|
|
407
|
+
def _run_podman_build(self, step: MigrationStep) -> None:
|
|
408
|
+
"""Run podman build on remote with periodic progress polling."""
|
|
409
|
+
cmd = step.command
|
|
410
|
+
if not cmd:
|
|
411
|
+
raise StepError(step, "No command specified")
|
|
412
|
+
|
|
413
|
+
timeout = step.timeout or 1800 # 30 min default for ARM64 builds
|
|
414
|
+
poll_interval = 15 # seconds between progress snapshots
|
|
415
|
+
done_event = threading.Event()
|
|
416
|
+
result_holder: list = []
|
|
417
|
+
|
|
418
|
+
def _ssh_build() -> None:
|
|
419
|
+
r = self.probe.run(cmd, timeout=timeout)
|
|
420
|
+
result_holder.append(r)
|
|
421
|
+
done_event.set()
|
|
422
|
+
|
|
423
|
+
thread = threading.Thread(target=_ssh_build, daemon=True)
|
|
424
|
+
thread.start()
|
|
425
|
+
|
|
426
|
+
elapsed = 0
|
|
427
|
+
while not done_event.wait(timeout=poll_interval):
|
|
428
|
+
elapsed += poll_interval
|
|
429
|
+
snap = self.probe.run(
|
|
430
|
+
"podman system df 2>/dev/null | grep -E 'Image|Cache' | "
|
|
431
|
+
"awk '{print $1, $3, $4}'",
|
|
432
|
+
timeout=10,
|
|
433
|
+
)
|
|
434
|
+
if snap.ok and snap.out.strip():
|
|
435
|
+
lines = " | ".join(snap.out.strip().splitlines())
|
|
436
|
+
msg = f"[{elapsed}s] podman cache: {lines}"
|
|
437
|
+
logger.debug(f" {msg}")
|
|
438
|
+
if self._emitter:
|
|
439
|
+
self._emitter.progress(step.id, msg)
|
|
440
|
+
else:
|
|
441
|
+
msg = f"[{elapsed}s] podman build in progress..."
|
|
442
|
+
logger.debug(f" {msg}")
|
|
443
|
+
if self._emitter:
|
|
444
|
+
self._emitter.progress(step.id, msg)
|
|
445
|
+
|
|
446
|
+
if not result_holder:
|
|
447
|
+
raise StepError(step, "Podman build thread did not return a result")
|
|
448
|
+
r = result_holder[0]
|
|
449
|
+
step.result = r.out[:500]
|
|
450
|
+
if not r.ok:
|
|
451
|
+
raise StepError(step, f"exit={r.exit_code}: {r.stderr[:300]}")
|
|
452
|
+
logger.debug(f" podman build completed in {elapsed + poll_interval}s")
|
|
453
|
+
step.status = StepStatus.DONE
|
|
454
|
+
|
|
402
455
|
def _run_docker_health_wait(self, step: MigrationStep) -> None:
|
|
403
456
|
"""Wait until all containers reach 'healthy' or 'running' status.
|
|
404
457
|
|
|
@@ -578,9 +631,10 @@ class Executor:
|
|
|
578
631
|
command_ref formats:
|
|
579
632
|
- "./file.md#section-id" - script from section in specific file
|
|
580
633
|
- "#section-id" - script from section in current spec file
|
|
634
|
+
- "#kiosk-browser-configuration-script" - markpact:ref block
|
|
581
635
|
"""
|
|
582
636
|
from pathlib import Path
|
|
583
|
-
from ..markpact.parser import extract_script_from_markdown
|
|
637
|
+
from ..markpact.parser import extract_script_from_markdown, extract_script_by_ref
|
|
584
638
|
|
|
585
639
|
# Parse command_ref
|
|
586
640
|
if "#" in command_ref:
|
|
@@ -597,12 +651,20 @@ class Executor:
|
|
|
597
651
|
if not file_path.exists():
|
|
598
652
|
raise StepError(step, f"Command ref file not found: {file_path}")
|
|
599
653
|
|
|
600
|
-
# Extract script from markdown
|
|
654
|
+
# Extract script from markdown - try both methods:
|
|
655
|
+
# 1. First try markpact:ref codeblock (new format)
|
|
656
|
+
# 2. Then try section heading (old format)
|
|
601
657
|
markdown_content = file_path.read_text(encoding="utf-8")
|
|
602
|
-
|
|
658
|
+
|
|
659
|
+
# Try markpact:ref format first
|
|
660
|
+
script = extract_script_by_ref(markdown_content, section_id, language="bash")
|
|
661
|
+
|
|
662
|
+
# Fallback to section heading format
|
|
663
|
+
if script is None:
|
|
664
|
+
script = extract_script_from_markdown(markdown_content, section_id, language="bash")
|
|
603
665
|
|
|
604
666
|
if script is None:
|
|
605
|
-
raise StepError(step, f"Could not find bash script
|
|
667
|
+
raise StepError(step, f"Could not find bash script for ref '{section_id}' in {file_path} (tried markpact:ref and section heading)")
|
|
606
668
|
|
|
607
669
|
return script
|
|
608
670
|
|