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