redeploy 0.1.6__tar.gz → 0.2.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.
Files changed (98) hide show
  1. {redeploy-0.1.6 → redeploy-0.2.4}/PKG-INFO +99 -12
  2. {redeploy-0.1.6 → redeploy-0.2.4}/README.md +98 -11
  3. {redeploy-0.1.6 → redeploy-0.2.4}/pyproject.toml +1 -1
  4. redeploy-0.2.4/redeploy/__init__.py +149 -0
  5. redeploy-0.2.4/redeploy/apply/executor.py +488 -0
  6. redeploy-0.2.4/redeploy/cli.py +2551 -0
  7. redeploy-0.2.4/redeploy/dsl/__init__.py +30 -0
  8. redeploy-0.2.4/redeploy/dsl/loader.py +403 -0
  9. redeploy-0.2.4/redeploy/dsl/parser.py +261 -0
  10. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/fleet.py +139 -0
  11. redeploy-0.2.4/redeploy/iac/__init__.py +47 -0
  12. redeploy-0.2.4/redeploy/iac/base.py +275 -0
  13. redeploy-0.2.4/redeploy/iac/docker_compose.py +418 -0
  14. redeploy-0.2.4/redeploy/iac/parsers/__init__.py +1 -0
  15. redeploy-0.2.4/redeploy/iac/parsers/compose.py +287 -0
  16. redeploy-0.2.4/redeploy/iac/registry.py +37 -0
  17. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/models.py +75 -4
  18. redeploy-0.2.4/redeploy/observe.py +313 -0
  19. redeploy-0.2.4/redeploy/patterns.py +407 -0
  20. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/plan/planner.py +133 -6
  21. redeploy-0.2.4/redeploy/plugins/__init__.py +180 -0
  22. redeploy-0.2.4/redeploy/plugins/builtin/__init__.py +1 -0
  23. redeploy-0.2.4/redeploy/plugins/builtin/browser_reload.py +120 -0
  24. redeploy-0.2.4/redeploy/plugins/builtin/notify.py +168 -0
  25. redeploy-0.2.4/redeploy/plugins/builtin/systemd_reload.py +98 -0
  26. redeploy-0.2.4/redeploy/tests/test_cli.py +230 -0
  27. redeploy-0.2.4/redeploy/tests/test_dsl.py +354 -0
  28. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_executor.py +140 -0
  29. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_fleet.py +254 -0
  30. redeploy-0.2.4/redeploy/tests/test_git_integration.py +325 -0
  31. redeploy-0.2.4/redeploy/tests/test_iac.py +461 -0
  32. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_models.py +11 -2
  33. redeploy-0.2.4/redeploy/tests/test_observe.py +369 -0
  34. redeploy-0.2.4/redeploy/tests/test_patterns.py +287 -0
  35. redeploy-0.2.4/redeploy/tests/test_plugins.py +410 -0
  36. redeploy-0.2.4/redeploy/tests/test_public_api.py +243 -0
  37. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_ssh.py +72 -0
  38. redeploy-0.2.4/redeploy/tests/test_version_changelog.py +356 -0
  39. redeploy-0.2.4/redeploy/tests/test_version_cli.py +172 -0
  40. redeploy-0.2.4/redeploy/tests/test_version_manifest.py +658 -0
  41. redeploy-0.2.4/redeploy/tests/test_version_sources.py +517 -0
  42. redeploy-0.2.4/redeploy/tests/test_workflow.py +286 -0
  43. redeploy-0.2.4/redeploy/version/__init__.py +148 -0
  44. redeploy-0.2.4/redeploy/version/bump.py +284 -0
  45. redeploy-0.2.4/redeploy/version/changelog.py +195 -0
  46. redeploy-0.2.4/redeploy/version/commits.py +218 -0
  47. redeploy-0.2.4/redeploy/version/diff.py +118 -0
  48. redeploy-0.2.4/redeploy/version/git_integration.py +142 -0
  49. redeploy-0.2.4/redeploy/version/git_transaction.py +105 -0
  50. redeploy-0.2.4/redeploy/version/manifest.py +169 -0
  51. redeploy-0.2.4/redeploy/version/sources/__init__.py +65 -0
  52. redeploy-0.2.4/redeploy/version/sources/base.py +44 -0
  53. redeploy-0.2.4/redeploy/version/sources/json_.py +94 -0
  54. redeploy-0.2.4/redeploy/version/sources/plain.py +28 -0
  55. redeploy-0.2.4/redeploy/version/sources/regex.py +69 -0
  56. redeploy-0.2.4/redeploy/version/sources/toml_.py +108 -0
  57. redeploy-0.2.4/redeploy/version/sources/yaml_.py +98 -0
  58. redeploy-0.2.4/redeploy/version/transaction.py +162 -0
  59. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy.egg-info/PKG-INFO +99 -12
  60. redeploy-0.2.4/redeploy.egg-info/SOURCES.txt +92 -0
  61. redeploy-0.2.4/tests/test_iac.py +511 -0
  62. redeploy-0.1.6/redeploy/__init__.py +0 -12
  63. redeploy-0.1.6/redeploy/apply/executor.py +0 -200
  64. redeploy-0.1.6/redeploy/cli.py +0 -1184
  65. redeploy-0.1.6/redeploy.egg-info/SOURCES.txt +0 -46
  66. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/apply/__init__.py +0 -0
  67. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/data_sync.py +0 -0
  68. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/detect/__init__.py +0 -0
  69. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/detect/detector.py +0 -0
  70. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/detect/probes.py +0 -0
  71. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/detect/remote.py +0 -0
  72. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/detect/templates.py +0 -0
  73. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/detect/workflow.py +0 -0
  74. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/discovery.py +0 -0
  75. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/parse.py +0 -0
  76. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/plan/__init__.py +0 -0
  77. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/ssh.py +0 -0
  78. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/steps.py +0 -0
  79. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/__init__.py +0 -0
  80. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_data_sync.py +0 -0
  81. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_discovery.py +0 -0
  82. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_examples.py +0 -0
  83. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_manifest.py +0 -0
  84. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_parse.py +0 -0
  85. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_planner_edge.py +0 -0
  86. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_probes.py +0 -0
  87. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_templates.py +0 -0
  88. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_verify.py +0 -0
  89. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/tests/test_version.py +0 -0
  90. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/verify.py +0 -0
  91. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy/version.py +0 -0
  92. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy.egg-info/dependency_links.txt +0 -0
  93. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy.egg-info/entry_points.txt +0 -0
  94. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy.egg-info/requires.txt +0 -0
  95. {redeploy-0.1.6 → redeploy-0.2.4}/redeploy.egg-info/top_level.txt +0 -0
  96. {redeploy-0.1.6 → redeploy-0.2.4}/setup.cfg +0 -0
  97. {redeploy-0.1.6 → redeploy-0.2.4}/tests/test_parse.py +0 -0
  98. {redeploy-0.1.6 → redeploy-0.2.4}/tests/test_redeploy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redeploy
3
- Version: 0.1.6
3
+ Version: 0.2.4
4
4
  Summary: Infrastructure migration toolkit: detect → plan → apply
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -26,17 +26,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
26
26
 
27
27
  ## AI Cost Tracking
28
28
 
29
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.6-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
30
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$1.95-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-3.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
29
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.4-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
30
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$3.30-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-5.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
31
31
 
32
- - 🤖 **LLM usage:** $1.9500 (13 commits)
33
- - 👤 **Human dev:** ~$333 (3.3h @ $100/h, 30min dedup)
32
+ - 🤖 **LLM usage:** $3.3000 (22 commits)
33
+ - 👤 **Human dev:** ~$533 (5.3h @ $100/h, 30min dedup)
34
34
 
35
35
  Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
36
36
 
37
37
  ---
38
38
 
39
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.1.6-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
39
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.4-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
40
40
 
41
41
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
42
42
 
@@ -162,8 +162,15 @@ Execute deploy from a YAML spec file (or `redeploy.yaml` project manifest if no
162
162
  | `--plan-only` | Show steps without connecting via SSH |
163
163
  | `--dry-run` | Connect, show steps, make no changes |
164
164
  | `--detect` | Live-probe host before planning (recommended for prod) |
165
+ | `--env NAME` | Use named environment from `redeploy.yaml` (e.g. `prod`, `rpi5`) |
165
166
  | `--plan-out FILE` | Save generated plan to file |
166
167
 
168
+ ```bash
169
+ redeploy run --env prod # use prod env from redeploy.yaml
170
+ redeploy run --env rpi5 --detect # deploy to rpi5 with live probe
171
+ redeploy run --dry-run # uses .env DEPLOY_* vars if no redeploy.yaml
172
+ ```
173
+
167
174
  ### `redeploy scan [options]`
168
175
 
169
176
  Discover SSH-accessible devices on the local network.
@@ -327,6 +334,11 @@ extra_steps:
327
334
  | `version_check` | `version_check` | Verify deployed version |
328
335
  | `sync_env` | `scp` | Copy .env to remote |
329
336
  | `podman_daemon_reload` | `systemctl_start` | `systemctl --user daemon-reload` |
337
+ | `stop_podman` | `systemctl_stop` | Stop all Podman containers via systemd |
338
+ | `enable_podman_unit` | `systemctl_start` | `systemctl daemon-reload && enable --now {service}.service` |
339
+ | `systemctl_restart` | `systemctl_start` | Restart a systemd service (`command=` to override) |
340
+ | `systemctl_daemon_reload` | `ssh_cmd` | `systemctl daemon-reload` |
341
+ | `git_pull` | `ssh_cmd` | `git pull --ff-only` with rollback (`git reset --hard HEAD@{1}`) |
330
342
 
331
343
  ### `insert_before`
332
344
 
@@ -338,17 +350,92 @@ extra_steps:
338
350
  insert_before: docker_build_pull # runs before build, not after verify
339
351
  ```
340
352
 
341
- ## `redeploy.yaml` project manifest
353
+ ## Plugin system
354
+
355
+ Extend the step pipeline with custom action types using `action: plugin`:
356
+
357
+ ```yaml
358
+ extra_steps:
359
+ - id: reload_kiosk
360
+ action: plugin
361
+ plugin_type: browser_reload
362
+ description: Reload kiosk browser after deploy
363
+ plugin_params:
364
+ port: 9222
365
+ ignore_cache: true
366
+ url_contains: "localhost:8100"
367
+ ```
368
+
369
+ ### Built-in plugins
370
+
371
+ | `plugin_type` | Description | `plugin_params` |
372
+ |---------------|-------------|-----------------|
373
+ | `browser_reload` | Reload Chromium via CDP (Chrome DevTools Protocol) over SSH | `port` (9222), `ignore_cache` (true), `url_contains` ("") |
374
+
375
+ ### Writing a custom plugin
376
+
377
+ Place a `.py` file in `./redeploy_plugins/` (project-local) or `~/.redeploy/plugins/` (user-global):
378
+
379
+ ```python
380
+ # ./redeploy_plugins/notify.py
381
+ from redeploy.plugins import register_plugin, PluginContext
382
+ from redeploy.models import StepStatus
383
+
384
+ @register_plugin("notify_slack")
385
+ def notify_slack(ctx: PluginContext) -> None:
386
+ webhook = ctx.params["webhook"]
387
+ ctx.probe.run(f"curl -X POST {webhook} -d '{{\"text\":\"deployed!\"}}'")
388
+ ctx.step.result = "notified"
389
+ ctx.step.status = StepStatus.DONE
390
+ ```
391
+
392
+ `PluginContext` fields:
393
+
394
+ | Field | Type | Description |
395
+ |-------|------|-------------|
396
+ | `step` | `MigrationStep` | Current step — set `result` and `status` here |
397
+ | `host` | `str` | SSH host (e.g. `pi@192.168.1.5`) |
398
+ | `probe` | `RemoteProbe` | Call `probe.run(cmd)` for remote SSH commands |
399
+ | `emitter` | `ProgressEmitter?` | Emit mid-step progress: `emitter.progress(step.id, msg)` |
400
+ | `params` | `dict` | Shortcut for `step.plugin_params` |
401
+ | `dry_run` | `bool` | Skip side-effects if True |
402
+
342
403
 
343
- Place in project root — `redeploy run` (no args) uses it automatically:
404
+
405
+ Place in project root — `redeploy run` (no args) uses it automatically.
406
+ Supports **named environments** for multi-target projects:
344
407
 
345
408
  ```yaml
346
409
  spec: migration.yaml # default spec file
347
- host: root@87.106.87.183
348
410
  app: myapp
349
- domain: myapp.example.com
350
- ssh_port: 22
351
- env_file: envs/vps.env
411
+
412
+ environments:
413
+ prod:
414
+ host: root@87.106.87.183
415
+ strategy: docker_full
416
+ domain: myapp.example.com
417
+ env_file: envs/vps.env
418
+ verify_url: https://myapp.example.com/api/v1/health
419
+ rpi5:
420
+ host: pi@192.168.188.108
421
+ strategy: systemd
422
+ env_file: .env
423
+ verify_url: http://192.168.188.108:8000/api/v1/health
424
+ dev:
425
+ host: local
426
+ strategy: docker_full
427
+ env_file: .env.local
428
+ verify_url: http://localhost:8000/api/v1/health
429
+ ```
430
+
431
+ Fallback: if no `redeploy.yaml` found, `redeploy run` reads `DEPLOY_*` vars from `.env`:
432
+
433
+ ```bash
434
+ # .env
435
+ DEPLOY_HOST=pi@192.168.1.5
436
+ DEPLOY_APP=myapp
437
+ DEPLOY_DOMAIN=myapp.local
438
+ DEPLOY_ENV_FILE=.env
352
439
  ```
353
440
 
354
441
  ## doql integration
@@ -3,17 +3,17 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.6-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$1.95-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-3.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.4-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$3.30-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-5.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $1.9500 (13 commits)
10
- - 👤 **Human dev:** ~$333 (3.3h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $3.3000 (22 commits)
10
+ - 👤 **Human dev:** ~$533 (5.3h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.1.6-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
16
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.4-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
17
17
 
18
18
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
19
19
 
@@ -139,8 +139,15 @@ Execute deploy from a YAML spec file (or `redeploy.yaml` project manifest if no
139
139
  | `--plan-only` | Show steps without connecting via SSH |
140
140
  | `--dry-run` | Connect, show steps, make no changes |
141
141
  | `--detect` | Live-probe host before planning (recommended for prod) |
142
+ | `--env NAME` | Use named environment from `redeploy.yaml` (e.g. `prod`, `rpi5`) |
142
143
  | `--plan-out FILE` | Save generated plan to file |
143
144
 
145
+ ```bash
146
+ redeploy run --env prod # use prod env from redeploy.yaml
147
+ redeploy run --env rpi5 --detect # deploy to rpi5 with live probe
148
+ redeploy run --dry-run # uses .env DEPLOY_* vars if no redeploy.yaml
149
+ ```
150
+
144
151
  ### `redeploy scan [options]`
145
152
 
146
153
  Discover SSH-accessible devices on the local network.
@@ -304,6 +311,11 @@ extra_steps:
304
311
  | `version_check` | `version_check` | Verify deployed version |
305
312
  | `sync_env` | `scp` | Copy .env to remote |
306
313
  | `podman_daemon_reload` | `systemctl_start` | `systemctl --user daemon-reload` |
314
+ | `stop_podman` | `systemctl_stop` | Stop all Podman containers via systemd |
315
+ | `enable_podman_unit` | `systemctl_start` | `systemctl daemon-reload && enable --now {service}.service` |
316
+ | `systemctl_restart` | `systemctl_start` | Restart a systemd service (`command=` to override) |
317
+ | `systemctl_daemon_reload` | `ssh_cmd` | `systemctl daemon-reload` |
318
+ | `git_pull` | `ssh_cmd` | `git pull --ff-only` with rollback (`git reset --hard HEAD@{1}`) |
307
319
 
308
320
  ### `insert_before`
309
321
 
@@ -315,17 +327,92 @@ extra_steps:
315
327
  insert_before: docker_build_pull # runs before build, not after verify
316
328
  ```
317
329
 
318
- ## `redeploy.yaml` project manifest
330
+ ## Plugin system
331
+
332
+ Extend the step pipeline with custom action types using `action: plugin`:
333
+
334
+ ```yaml
335
+ extra_steps:
336
+ - id: reload_kiosk
337
+ action: plugin
338
+ plugin_type: browser_reload
339
+ description: Reload kiosk browser after deploy
340
+ plugin_params:
341
+ port: 9222
342
+ ignore_cache: true
343
+ url_contains: "localhost:8100"
344
+ ```
345
+
346
+ ### Built-in plugins
347
+
348
+ | `plugin_type` | Description | `plugin_params` |
349
+ |---------------|-------------|-----------------|
350
+ | `browser_reload` | Reload Chromium via CDP (Chrome DevTools Protocol) over SSH | `port` (9222), `ignore_cache` (true), `url_contains` ("") |
351
+
352
+ ### Writing a custom plugin
353
+
354
+ Place a `.py` file in `./redeploy_plugins/` (project-local) or `~/.redeploy/plugins/` (user-global):
355
+
356
+ ```python
357
+ # ./redeploy_plugins/notify.py
358
+ from redeploy.plugins import register_plugin, PluginContext
359
+ from redeploy.models import StepStatus
360
+
361
+ @register_plugin("notify_slack")
362
+ def notify_slack(ctx: PluginContext) -> None:
363
+ webhook = ctx.params["webhook"]
364
+ ctx.probe.run(f"curl -X POST {webhook} -d '{{\"text\":\"deployed!\"}}'")
365
+ ctx.step.result = "notified"
366
+ ctx.step.status = StepStatus.DONE
367
+ ```
368
+
369
+ `PluginContext` fields:
370
+
371
+ | Field | Type | Description |
372
+ |-------|------|-------------|
373
+ | `step` | `MigrationStep` | Current step — set `result` and `status` here |
374
+ | `host` | `str` | SSH host (e.g. `pi@192.168.1.5`) |
375
+ | `probe` | `RemoteProbe` | Call `probe.run(cmd)` for remote SSH commands |
376
+ | `emitter` | `ProgressEmitter?` | Emit mid-step progress: `emitter.progress(step.id, msg)` |
377
+ | `params` | `dict` | Shortcut for `step.plugin_params` |
378
+ | `dry_run` | `bool` | Skip side-effects if True |
379
+
319
380
 
320
- Place in project root — `redeploy run` (no args) uses it automatically:
381
+
382
+ Place in project root — `redeploy run` (no args) uses it automatically.
383
+ Supports **named environments** for multi-target projects:
321
384
 
322
385
  ```yaml
323
386
  spec: migration.yaml # default spec file
324
- host: root@87.106.87.183
325
387
  app: myapp
326
- domain: myapp.example.com
327
- ssh_port: 22
328
- env_file: envs/vps.env
388
+
389
+ environments:
390
+ prod:
391
+ host: root@87.106.87.183
392
+ strategy: docker_full
393
+ domain: myapp.example.com
394
+ env_file: envs/vps.env
395
+ verify_url: https://myapp.example.com/api/v1/health
396
+ rpi5:
397
+ host: pi@192.168.188.108
398
+ strategy: systemd
399
+ env_file: .env
400
+ verify_url: http://192.168.188.108:8000/api/v1/health
401
+ dev:
402
+ host: local
403
+ strategy: docker_full
404
+ env_file: .env.local
405
+ verify_url: http://localhost:8000/api/v1/health
406
+ ```
407
+
408
+ Fallback: if no `redeploy.yaml` found, `redeploy run` reads `DEPLOY_*` vars from `.env`:
409
+
410
+ ```bash
411
+ # .env
412
+ DEPLOY_HOST=pi@192.168.1.5
413
+ DEPLOY_APP=myapp
414
+ DEPLOY_DOMAIN=myapp.local
415
+ DEPLOY_ENV_FILE=.env
329
416
  ```
330
417
 
331
418
  ## doql integration
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "redeploy"
7
- version = "0.1.6"
7
+ version = "0.2.4"
8
8
  description = "Infrastructure migration toolkit: detect → plan → apply"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,149 @@
1
+ """redeploy — Infrastructure migration toolkit: detect → plan → apply.
2
+
3
+ Public API (stable from 0.2.0, semver guaranteed)::
4
+
5
+ from redeploy import MigrationSpec, Planner, Executor
6
+ from redeploy import InfraState, TargetConfig, DeployStrategy
7
+
8
+ Everything not listed in ``__all__`` is internal and may change without notice.
9
+ """
10
+ __version__ = "0.2.4"
11
+
12
+ # ── Core models ───────────────────────────────────────────────────────────────
13
+ from .models import ( # noqa: F401
14
+ ConflictSeverity,
15
+ DeployStrategy,
16
+ InfraSpec,
17
+ InfraState,
18
+ MigrationPlan,
19
+ MigrationSpec,
20
+ MigrationStep,
21
+ StepAction,
22
+ StepStatus,
23
+ TargetConfig,
24
+ )
25
+
26
+ # ── Pipeline ──────────────────────────────────────────────────────────────────
27
+ from .detect import Detector # noqa: F401
28
+ from .plan import Planner # noqa: F401
29
+ from .apply import Executor # noqa: F401
30
+
31
+ # ── SSH primitives ────────────────────────────────────────────────────────────
32
+ from .ssh import SshClient, SshResult # noqa: F401
33
+
34
+ # ── Fleet / registry (first-class from 0.2.0) ────────────────────────────────
35
+ from .models import DeviceRegistry, KnownDevice # noqa: F401
36
+ from .fleet import ( # noqa: F401
37
+ DeviceArch,
38
+ DeviceExpectation,
39
+ Fleet,
40
+ FleetConfig,
41
+ FleetDevice,
42
+ Stage,
43
+ STAGE_DEFAULT_EXPECTATIONS,
44
+ )
45
+ from .steps import StepLibrary # noqa: F401
46
+
47
+ # ── Deploy patterns (first-class from 0.2.0) ──────────────────────────────────
48
+ from .patterns import ( # noqa: F401
49
+ DeployPattern,
50
+ BlueGreenPattern,
51
+ CanaryPattern,
52
+ RollbackOnFailurePattern,
53
+ get_pattern,
54
+ list_patterns,
55
+ pattern_registry,
56
+ )
57
+
58
+ # ── Observability (first-class from 0.2.0) ────────────────────────────────────
59
+ from .observe import ( # noqa: F401
60
+ AuditEntry,
61
+ DeployAuditLog,
62
+ DeployReport,
63
+ )
64
+
65
+ # ── IaC parsers (first-class from 0.3.0) ──────────────────────────────────────
66
+ from .iac import ( # noqa: F401
67
+ ParsedSpec,
68
+ Parser,
69
+ ParserRegistry,
70
+ PortInfo,
71
+ ServiceInfo,
72
+ VolumeInfo,
73
+ ConversionWarning,
74
+ parse_file,
75
+ parse_dir,
76
+ parser_registry,
77
+ )
78
+
79
+ # ── Plugin system (first-class from 0.2.2) ────────────────────────────────────
80
+ from .plugins import ( # noqa: F401
81
+ PluginContext,
82
+ PluginRegistry,
83
+ register_plugin,
84
+ load_user_plugins,
85
+ registry as plugin_registry,
86
+ )
87
+
88
+ __all__ = [
89
+ # version
90
+ "__version__",
91
+ # models
92
+ "ConflictSeverity",
93
+ "DeployStrategy",
94
+ "InfraSpec",
95
+ "InfraState",
96
+ "MigrationPlan",
97
+ "MigrationSpec",
98
+ "MigrationStep",
99
+ "StepAction",
100
+ "StepStatus",
101
+ "TargetConfig",
102
+ # pipeline
103
+ "Detector",
104
+ "Planner",
105
+ "Executor",
106
+ # SSH
107
+ "SshClient",
108
+ "SshResult",
109
+ # fleet / registry
110
+ "DeviceRegistry",
111
+ "KnownDevice",
112
+ "Fleet",
113
+ "DeviceArch",
114
+ "DeviceExpectation",
115
+ "FleetConfig",
116
+ "FleetDevice",
117
+ "Stage",
118
+ "STAGE_DEFAULT_EXPECTATIONS",
119
+ "StepLibrary",
120
+ # patterns
121
+ "DeployPattern",
122
+ "BlueGreenPattern",
123
+ "CanaryPattern",
124
+ "RollbackOnFailurePattern",
125
+ "get_pattern",
126
+ "list_patterns",
127
+ "pattern_registry",
128
+ # observability
129
+ "AuditEntry",
130
+ "DeployAuditLog",
131
+ "DeployReport",
132
+ # iac parsers
133
+ "ParsedSpec",
134
+ "Parser",
135
+ "ParserRegistry",
136
+ "PortInfo",
137
+ "ServiceInfo",
138
+ "VolumeInfo",
139
+ "ConversionWarning",
140
+ "parse_file",
141
+ "parse_dir",
142
+ "parser_registry",
143
+ # plugin system
144
+ "PluginContext",
145
+ "PluginRegistry",
146
+ "register_plugin",
147
+ "load_user_plugins",
148
+ "plugin_registry",
149
+ ]