redeploy 0.1.2__tar.gz → 0.1.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 (54) hide show
  1. redeploy-0.1.4/PKG-INFO +402 -0
  2. redeploy-0.1.4/README.md +379 -0
  3. {redeploy-0.1.2 → redeploy-0.1.4}/pyproject.toml +1 -1
  4. redeploy-0.1.4/redeploy/__init__.py +12 -0
  5. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/apply/executor.py +18 -18
  6. redeploy-0.1.4/redeploy/cli.py +1184 -0
  7. redeploy-0.1.4/redeploy/detect/templates.py +459 -0
  8. redeploy-0.1.4/redeploy/detect/workflow.py +418 -0
  9. redeploy-0.1.4/redeploy/discovery.py +665 -0
  10. redeploy-0.1.4/redeploy/fleet.py +257 -0
  11. redeploy-0.1.4/redeploy/models.py +560 -0
  12. redeploy-0.1.4/redeploy/parse.py +169 -0
  13. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/plan/planner.py +185 -10
  14. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/ssh.py +28 -0
  15. redeploy-0.1.4/redeploy/steps.py +294 -0
  16. redeploy-0.1.4/redeploy/tests/test_data_sync.py +117 -0
  17. redeploy-0.1.4/redeploy/tests/test_discovery.py +231 -0
  18. redeploy-0.1.4/redeploy/tests/test_examples.py +489 -0
  19. redeploy-0.1.4/redeploy/tests/test_executor.py +478 -0
  20. redeploy-0.1.4/redeploy/tests/test_fleet.py +271 -0
  21. redeploy-0.1.4/redeploy/tests/test_manifest.py +338 -0
  22. redeploy-0.1.4/redeploy/tests/test_parse.py +268 -0
  23. redeploy-0.1.4/redeploy/tests/test_planner_edge.py +350 -0
  24. redeploy-0.1.4/redeploy/tests/test_probes.py +346 -0
  25. redeploy-0.1.4/redeploy/tests/test_templates.py +424 -0
  26. redeploy-0.1.4/redeploy/tests/test_verify.py +218 -0
  27. redeploy-0.1.4/redeploy/tests/test_version.py +219 -0
  28. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/version.py +3 -2
  29. redeploy-0.1.4/redeploy.egg-info/PKG-INFO +402 -0
  30. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy.egg-info/SOURCES.txt +19 -0
  31. redeploy-0.1.4/tests/test_parse.py +157 -0
  32. redeploy-0.1.2/PKG-INFO +0 -168
  33. redeploy-0.1.2/README.md +0 -145
  34. redeploy-0.1.2/redeploy/__init__.py +0 -2
  35. redeploy-0.1.2/redeploy/cli.py +0 -383
  36. redeploy-0.1.2/redeploy/models.py +0 -271
  37. redeploy-0.1.2/redeploy.egg-info/PKG-INFO +0 -168
  38. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/apply/__init__.py +0 -0
  39. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/data_sync.py +0 -0
  40. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/detect/__init__.py +0 -0
  41. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/detect/detector.py +0 -0
  42. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/detect/probes.py +0 -0
  43. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/detect/remote.py +0 -0
  44. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/plan/__init__.py +0 -0
  45. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/tests/__init__.py +0 -0
  46. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/tests/test_models.py +0 -0
  47. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/tests/test_ssh.py +0 -0
  48. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy/verify.py +0 -0
  49. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy.egg-info/dependency_links.txt +0 -0
  50. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy.egg-info/entry_points.txt +0 -0
  51. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy.egg-info/requires.txt +0 -0
  52. {redeploy-0.1.2 → redeploy-0.1.4}/redeploy.egg-info/top_level.txt +0 -0
  53. {redeploy-0.1.2 → redeploy-0.1.4}/setup.cfg +0 -0
  54. {redeploy-0.1.2 → redeploy-0.1.4}/tests/test_redeploy.py +0 -0
@@ -0,0 +1,402 @@
1
+ Metadata-Version: 2.4
2
+ Name: redeploy
3
+ Version: 0.1.4
4
+ Summary: Infrastructure migration toolkit: detect → plan → apply
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pydantic>=2.0
8
+ Requires-Dist: pyyaml>=6.0
9
+ Requires-Dist: click>=8.0
10
+ Requires-Dist: loguru>=0.7
11
+ Requires-Dist: paramiko>=3.0
12
+ Requires-Dist: httpx>=0.25
13
+ Requires-Dist: rich>=13.0
14
+ Requires-Dist: goal>=2.1.0
15
+ Requires-Dist: costs>=0.1.20
16
+ Requires-Dist: pfix>=0.1.60
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=8.0; extra == "dev"
19
+ Requires-Dist: ruff>=0.4; extra == "dev"
20
+ Requires-Dist: goal>=2.1.0; extra == "dev"
21
+ Requires-Dist: costs>=0.1.20; extra == "dev"
22
+ Requires-Dist: pfix>=0.1.60; extra == "dev"
23
+
24
+ # redeploy
25
+
26
+
27
+ ## AI Cost Tracking
28
+
29
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.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-$1.65-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)
31
+
32
+ - 🤖 **LLM usage:** $1.6500 (11 commits)
33
+ - 👤 **Human dev:** ~$326 (3.3h @ $100/h, 30min dedup)
34
+
35
+ Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
36
+
37
+ ---
38
+
39
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.1.4-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
40
+
41
+ Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
42
+
43
+ ```
44
+ redeploy detect → live probe host (what is there now)
45
+ redeploy plan → migration-plan.yaml (what to do)
46
+ redeploy apply → execute plan (do it)
47
+ redeploy run → detect + plan + apply (all at once from spec)
48
+ redeploy scan → find devices on LAN (device registry)
49
+ redeploy target → deploy to named device (fleet)
50
+ ```
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ # Recommended — installs CLI globally (no venv conflicts)
56
+ pipx install redeploy
57
+
58
+ # Or inside a venv
59
+ pip install redeploy
60
+
61
+ # With doql integration (generates migration.yaml from app.doql):
62
+ pip install doql[deploy]
63
+ ```
64
+
65
+ ## Quick start — VPS production deploy
66
+
67
+ ```bash
68
+ # 1. Create spec file
69
+ cat > migration.yaml << 'EOF'
70
+ name: "myapp deploy 1.0.19 → 1.0.20"
71
+ source:
72
+ strategy: docker_full
73
+ host: root@YOUR_VPS_IP
74
+ app: myapp
75
+ version: "1.0.19"
76
+ target:
77
+ strategy: docker_full
78
+ host: root@YOUR_VPS_IP
79
+ app: myapp
80
+ version: "1.0.20"
81
+ domain: myapp.example.com
82
+ env_file: envs/prod.env
83
+ compose_files:
84
+ - docker-compose.prod.yml
85
+ verify_url: https://myapp.example.com/api/v1/health
86
+ verify_version: "1.0.20"
87
+ EOF
88
+
89
+ # 2. Preview steps (no SSH needed)
90
+ redeploy run migration.yaml --plan-only
91
+
92
+ # 3. Dry run (connects via SSH, makes no changes)
93
+ redeploy run migration.yaml --dry-run
94
+
95
+ # 4. Full deploy (live detect → plan → apply)
96
+ redeploy run migration.yaml --detect
97
+
98
+ # Or without --detect (faster, uses spec source as-is)
99
+ redeploy run migration.yaml
100
+ ```
101
+
102
+ ## Quick start — Raspberry Pi kiosk
103
+
104
+ ```bash
105
+ # Register the RPi in the device registry
106
+ redeploy device-add pi@192.168.1.42 \
107
+ --tag kiosk --tag rpi4 \
108
+ --strategy native_kiosk \
109
+ --app kiosk-app \
110
+ --name "Workshop kiosk #1"
111
+
112
+ # Preview deploy plan
113
+ redeploy target pi@192.168.1.42 migration.yaml --plan-only
114
+
115
+ # Dry run
116
+ redeploy target pi@192.168.1.42 migration.yaml --dry-run
117
+
118
+ # Deploy
119
+ redeploy target pi@192.168.1.42 migration.yaml --detect
120
+ ```
121
+
122
+ ## Device registry — find and manage devices
123
+
124
+ ```bash
125
+ # Discover SSH-accessible devices on local network (passive: known_hosts + ARP + mDNS)
126
+ redeploy scan
127
+
128
+ # Active ICMP ping sweep (sends packets)
129
+ redeploy scan --ping --subnet 192.168.1.0/24
130
+
131
+ # Try specific SSH users
132
+ redeploy scan --user pi --user ubuntu --timeout 8
133
+
134
+ # List all known devices
135
+ redeploy devices
136
+
137
+ # Filter by tag or strategy
138
+ redeploy devices --tag kiosk
139
+ redeploy devices --strategy native_kiosk
140
+ redeploy devices --reachable # seen in last 5 minutes
141
+
142
+ # JSON output for scripting
143
+ redeploy devices --json | jq '.[] | select(.tags | index("prod"))'
144
+
145
+ # Add device manually
146
+ redeploy device-add root@10.0.0.5 --tag prod --strategy docker_full --app myapp
147
+
148
+ # Remove device
149
+ redeploy device-rm root@10.0.0.5
150
+ ```
151
+
152
+ Registry is stored at `~/.config/redeploy/devices.yaml` (chmod 600 — safe for SSH key paths).
153
+
154
+ ## CLI reference
155
+
156
+ ### `redeploy run SPEC [options]`
157
+
158
+ Execute deploy from a YAML spec file (or `redeploy.yaml` project manifest if no arg).
159
+
160
+ | Option | Description |
161
+ |--------|-------------|
162
+ | `--plan-only` | Show steps without connecting via SSH |
163
+ | `--dry-run` | Connect, show steps, make no changes |
164
+ | `--detect` | Live-probe host before planning (recommended for prod) |
165
+ | `--plan-out FILE` | Save generated plan to file |
166
+
167
+ ### `redeploy scan [options]`
168
+
169
+ Discover SSH-accessible devices on the local network.
170
+
171
+ | Source | Network activity | Requires |
172
+ |--------|-----------------|---------|
173
+ | `known_hosts` | none | `~/.ssh/known_hosts` |
174
+ | `arp` | none | `ip neigh` / `arp -a` |
175
+ | `mdns` | passive listen | `avahi-browse` |
176
+ | `ping_sweep` | ICMP — **active** | `--ping` flag |
177
+
178
+ All SSH-reachable devices are saved to registry. Existing entries updated (last_seen, mac, hostname). Old entries never deleted.
179
+
180
+ ### `redeploy target DEVICE_ID [SPEC] [options]`
181
+
182
+ Deploy a spec to a registered device. Device's `host`, `strategy`, `app`, `domain` are overlaid onto the spec.
183
+
184
+ ```bash
185
+ redeploy target pi@192.168.1.42 # uses migration.yaml in cwd
186
+ redeploy target pi@192.168.1.42 custom.yaml --dry-run
187
+ redeploy target prod-vps --detect --plan-only
188
+ ```
189
+
190
+ After successful deploy, a `DeployRecord` is saved to the device in registry (timestamp, strategy, version, ok/fail).
191
+
192
+ ### `redeploy detect / plan / apply / migrate / init / status`
193
+
194
+ ```bash
195
+ redeploy detect --host root@VPS_IP --app myapp -o infra.yaml
196
+ redeploy plan --infra infra.yaml --target target.yaml -o plan.yaml
197
+ redeploy apply --plan plan.yaml
198
+ redeploy migrate --host root@VPS_IP --app myapp --target target.yaml # all in one
199
+ redeploy init # scaffold migration.yaml + redeploy.yaml
200
+ redeploy status # show project manifest summary
201
+ ```
202
+
203
+ ## Deployment strategies
204
+
205
+ | Strategy | Description | Use case |
206
+ |----------|-------------|----------|
207
+ | `docker_full` | Docker Compose — build + up | VPS production |
208
+ | `podman_quadlet` | Rootless Podman systemd units | Quadlet/rootless VPS |
209
+ | `native_kiosk` | systemd + Chromium Openbox | RPi kiosk (no Docker) |
210
+ | `docker_kiosk` | Podman Quadlet in kiosk mode | RPi kiosk with container |
211
+ | `k3s` | Kubernetes/k3s | K3s cluster |
212
+ | `systemd` | Native systemd service | Bare metal |
213
+
214
+ ### `native_kiosk` plan steps
215
+
216
+ Generated automatically when `strategy: native_kiosk`:
217
+
218
+ ```
219
+ rsync_build → sync build/ to device
220
+ run_kiosk_installer → bash build/infra/install-kiosk.sh
221
+ install_kiosk_service → scp kiosk.service → /etc/systemd/system/
222
+ enable_kiosk_service → systemctl enable --now
223
+ wait_kiosk_start → 20s
224
+ http_health_check → curl http://localhost:8080
225
+ ```
226
+
227
+ ### `docker_kiosk` plan steps
228
+
229
+ ```
230
+ rsync_build → sync build/ to device
231
+ install_kiosk_quadlet → cp *.container → ~/.config/containers/systemd/ + daemon-reload
232
+ start_kiosk_container → systemctl --user restart app.service
233
+ wait_kiosk_start → 20s
234
+ http_health_check → curl http://localhost:8080
235
+ ```
236
+
237
+ ### `podman_quadlet` plan steps
238
+
239
+ ```
240
+ sync_env → scp .env to remote
241
+ install_quadlet_files → cp *.container *.network *.volume → ~/.config/containers/systemd/
242
+ podman_daemon_reload → systemctl --user daemon-reload
243
+ stop_<app> → systemctl --user stop <app>.service
244
+ start_<app> → systemctl --user start <app>.service
245
+ wait_startup → 15s
246
+ http_health_check → verify_url health endpoint
247
+ version_check → verify_version match
248
+ ```
249
+
250
+ For system (root) mode, set `stop_services: true` in `target` — switches to `systemctl` (no `--user`) and `/etc/containers/systemd/`.
251
+
252
+ ### `docker_full` plan steps
253
+
254
+ ```
255
+ sync_env → scp env_file → remote_dir/.env
256
+ docker_build_pull → docker compose build (on remote)
257
+ docker_compose_up → docker compose up -d --build
258
+ wait_startup → 30s
259
+ http_health_check → verify_url health endpoint
260
+ version_check → verify_version match
261
+ ```
262
+
263
+ ## `migration.yaml` spec format
264
+
265
+ ```yaml
266
+ name: "myapp deploy 1.0.19 → 1.0.20"
267
+ description: "Production VPS version bump"
268
+
269
+ source:
270
+ strategy: docker_full # docker_full | podman_quadlet | native_kiosk | docker_kiosk | k3s | systemd
271
+ host: root@87.106.87.183 # SSH target (user@ip) or "local"
272
+ app: myapp
273
+ version: "1.0.19"
274
+ domain: myapp.example.com
275
+ remote_dir: ~/myapp
276
+
277
+ target:
278
+ strategy: docker_full
279
+ host: root@87.106.87.183
280
+ app: myapp
281
+ version: "1.0.20"
282
+ domain: myapp.example.com
283
+ remote_dir: ~/myapp
284
+ compose_files:
285
+ - docker-compose.vps.yml
286
+ env_file: envs/vps.env
287
+ verify_url: https://myapp.example.com/api/v1/health
288
+ verify_version: "1.0.20"
289
+
290
+ extra_steps: # optional — appended or inserted
291
+ - id: flush_k3s_iptables # StepLibrary name — no action needed
292
+ insert_before: docker_build_pull # inject before specific step
293
+ - id: docker_prune # StepLibrary: prune unused images
294
+ - id: notify_slack # custom step (needs action:)
295
+ action: ssh_cmd
296
+ description: "Send deploy notification"
297
+ command: "curl -s -X POST $SLACK_WEBHOOK -d '{\"text\":\"deployed 1.0.20\"}'"
298
+ risk: low
299
+ ```
300
+
301
+ ## StepLibrary — reusable named steps
302
+
303
+ Reference any step by `id` alone — no `action` needed. Fields can be overridden:
304
+
305
+ ```yaml
306
+ extra_steps:
307
+ - id: flush_k3s_iptables # use as-is
308
+ - id: stop_k3s
309
+ - id: http_health_check
310
+ url: https://myapp.example.com/health # override url
311
+ - id: wait_startup_long # 60s instead of 30s
312
+ ```
313
+
314
+ | ID | Action | Description |
315
+ |----|--------|-------------|
316
+ | `flush_k3s_iptables` | `ssh_cmd` | Flush CNI-HOSTPORT-DNAT + KUBE-* chains (stale k3s rules block Docker-proxy on 80/443) |
317
+ | `delete_k3s_ingresses` | `kubectl_delete` | Delete all k3s ingresses |
318
+ | `stop_k3s` | `systemctl_stop` | Stop k3s service |
319
+ | `disable_k3s` | `systemctl_disable` | Disable k3s on boot |
320
+ | `stop_nginx` | `systemctl_stop` | Stop host nginx (port 80 conflict) |
321
+ | `restart_traefik` | `ssh_cmd` | Restart Traefik container |
322
+ | `docker_prune` | `ssh_cmd` | Prune unused images + build cache |
323
+ | `docker_compose_down` | `docker_compose_down` | Stop Docker Compose stack |
324
+ | `wait_startup` | `wait` | Wait 30s |
325
+ | `wait_startup_long` | `wait` | Wait 60s |
326
+ | `http_health_check` | `http_check` | Verify health endpoint (`expect: healthy`) |
327
+ | `version_check` | `version_check` | Verify deployed version |
328
+ | `sync_env` | `scp` | Copy .env to remote |
329
+ | `podman_daemon_reload` | `systemctl_start` | `systemctl --user daemon-reload` |
330
+
331
+ ### `insert_before`
332
+
333
+ By default extra steps are appended after all generated steps. Use `insert_before: <step_id>` to inject at a specific position:
334
+
335
+ ```yaml
336
+ extra_steps:
337
+ - id: flush_k3s_iptables
338
+ insert_before: docker_build_pull # runs before build, not after verify
339
+ ```
340
+
341
+ ## `redeploy.yaml` project manifest
342
+
343
+ Place in project root — `redeploy run` (no args) uses it automatically:
344
+
345
+ ```yaml
346
+ spec: migration.yaml # default spec file
347
+ host: root@87.106.87.183
348
+ app: myapp
349
+ domain: myapp.example.com
350
+ ssh_port: 22
351
+ env_file: envs/vps.env
352
+ ```
353
+
354
+ ## doql integration
355
+
356
+ redeploy is the deploy engine for [doql](https://github.com/softreck/doql) declarative apps.
357
+
358
+ ```bash
359
+ # Install with doql integration
360
+ pip install doql[deploy]
361
+
362
+ # doql build generates build/infra/migration.yaml automatically
363
+ DEPLOY_HOST=root@YOUR_VPS doql build
364
+
365
+ # Then deploy — no args needed
366
+ doql deploy # calls redeploy API internally
367
+ doql deploy --plan-only
368
+ doql deploy --dry-run
369
+ doql quadlet --install # installs Quadlet units via redeploy
370
+ ```
371
+
372
+ doql `DEPLOY.target` → redeploy `strategy` mapping:
373
+
374
+ | doql | redeploy |
375
+ |------|---------|
376
+ | `docker-compose` | `docker_full` |
377
+ | `quadlet` | `podman_quadlet` |
378
+ | `kiosk-appliance` | `native_kiosk` |
379
+ | `kubernetes` | `k3s` |
380
+
381
+ ## Examples
382
+
383
+ | Directory | Scenario | Strategy |
384
+ |-----------|----------|----------|
385
+ | `01-vps-version-bump` | VPS Docker version bump | `docker_full → docker_full` |
386
+ | `02-k3s-to-docker` | Migrate off k3s | `k3s → docker_full` |
387
+ | `03-docker-to-podman-quadlet` | Move to rootless Podman | `docker_full → podman_quadlet` |
388
+ | `04-rpi-kiosk` | Raspberry Pi kiosk update | `native_kiosk → native_kiosk` |
389
+ | `05-iot-fleet-ota` | IoT fleet OTA update | `docker_full → docker_full` |
390
+ | `09-fleet-yaml` | Fleet with stages + scan | fleet + `redeploy target` |
391
+ | `11-traefik-tls` | Traefik + Let's Encrypt | `docker_full → podman_quadlet` |
392
+ | `12-ci-pipeline` | GitHub Actions / GitLab CI | CI-triggered `docker_full` |
393
+
394
+ ```bash
395
+ # Run any example in dry-run mode (no SSH required):
396
+ redeploy run examples/01-vps-version-bump/migration.yaml --plan-only
397
+ redeploy run examples/04-rpi-kiosk/migration.yaml --plan-only
398
+ ```
399
+
400
+ ## License
401
+
402
+ Licensed under Apache-2.0.