Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

143 Commits

Repository files navigation

redeploy

AI Cost Tracking

PyPIVersionPythonLicenseAI CostHuman TimeModel

  • 🤖 LLM usage: $6.7749 (101 commits)
  • 👤 Human dev: ~$4201 (42.0h @ $100/h, 30min dedup)

Generated on 2026-05-20 using openrouter/qwen/qwen3-coder-next


PyPIVersionPythonLicense

Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.

redeploy detect → live probe host (what is there now)
redeploy plan → migration-plan.yaml (what to do)
redeploy apply → execute plan (do it)
redeploy run → detect + plan + apply (all at once from spec)
redeploy fix → bump + run + LLM heal (smart self-healing deploy)
redeploy bump → bump version in spec (patch/minor/major)
redeploy prompt → NLP → command via LLM (natural language interface)
redeploy mcp → start MCP server (Claude Desktop / VS Code / remote API)
redeploy scan → find devices on LAN (device registry)
redeploy target → deploy to named device (fleet)
redeploy gh-workflow → analyze/run GitHub Actions workflows on demand

Install

# Recommended — installs CLI globally (no venv conflicts)
pipx install redeploy
# Or inside a venv
pip install redeploy
# With doql integration (generates migration.yaml from app.doql):
pip install doql[deploy]

Quick start — VPS production deploy

# 1. Create spec file
cat > migration.yaml << 'EOF'name: "myapp deploy 1.0.19 → 1.0.20"source: strategy: docker_full host: root@YOUR_VPS_IP app: myapp version: "1.0.19"target: strategy: docker_full host: root@YOUR_VPS_IP app: myapp version: "1.0.20" domain: myapp.example.com env_file: envs/prod.env compose_files: - docker-compose.prod.yml verify_url: https://myapp.example.com/api/v1/health verify_version: "1.0.20"EOF# 2. Preview steps (no SSH needed)
redeploy run migration.yaml --plan-only
# 3. Dry run (connects via SSH, makes no changes)
redeploy run migration.yaml --dry-run
# 4. Full deploy (live detect → plan → apply)
redeploy run migration.yaml --detect
# Or without --detect (faster, uses spec source as-is)
redeploy run migration.yaml

Quick start — Raspberry Pi kiosk

# Register the RPi in the device registry
redeploy device-add pi@192.168.1.42 \
--tag kiosk --tag rpi4 \
--strategy native_kiosk \
--app kiosk-app \
--name "Workshop kiosk #1"# Preview deploy plan
redeploy target pi@192.168.1.42 migration.yaml --plan-only
# Dry run
redeploy target pi@192.168.1.42 migration.yaml --dry-run
# Deploy
redeploy target pi@192.168.1.42 migration.yaml --detect

Device registry — find and manage devices

# Discover SSH-accessible devices on local network (passive: known_hosts + ARP + mDNS)
redeploy scan
# Active ICMP ping sweep (sends packets)
redeploy scan --ping --subnet 192.168.1.0/24
# Try specific SSH users
redeploy scan --user pi --user ubuntu --timeout 8
# List all known devices
redeploy devices
# Filter by tag or strategy
redeploy devices --tag kiosk
redeploy devices --strategy native_kiosk
redeploy devices --reachable # seen in last 5 minutes# JSON output for scripting
redeploy devices --json | jq '.[] | select(.tags | index("prod"))'# Add device manually
redeploy device-add root@10.0.0.5 --tag prod --strategy docker_full --app myapp
# Remove device
redeploy device-rm root@10.0.0.5

Registry is stored at ~/.config/redeploy/devices.yaml (chmod 600 — safe for SSH key paths).

Declarative config workflow

redeploy supports a declarative configuration workflow for hardware settings — scan to YAML, edit locally, and apply to device:

# 1. Scan hardware state to YAML
redeploy hardware pi@192.168.188.109 > hardware.yaml
redeploy device-map pi@192.168.188.109 > device-map.yaml
# 2. Edit YAML locally (e.g., change display rotation)# hardware.yaml:# drm_outputs:# - name: card0-DSI-2# connector: DSI-2# transform: '270' # ← edit this value# 3. Apply config to remote device
redeploy hardware pi@192.168.188.109 --apply-config hardware.yaml
redeploy device-map pi@192.168.188.109 --apply-config device-map.yaml

What --apply-config does:

  • Applies display transforms via wlr-randr (Wayland compositor)
  • Updates kanshi config (~/.config/kanshi/config) for persistent display rotation
  • Sets backlight brightness and power state
  • Supports both YAML and JSON config files

Supported commands:

  • redeploy hardware --apply-config FILE
  • redeploy device-map --apply-config FILE
  • redeploy blueprint show FILE --apply-config FILE

JMESPath query support

Extract specific values from YAML/JSON output using JMESPath query language (similar to XPath for XML):

# Simple path queries
redeploy hardware pi@192.168.188.109 --query "drm_outputs[0].transform"
redeploy hardware pi@192.168.188.109 --query "kernel"
redeploy device-map pi@192.168.188.109 --query "host"# Filter queries
redeploy hardware pi@192.168.188.109 --query "backlights[?name==\`11-0045\`].brightness"
redeploy hardware pi@192.168.188.109 --query "drm_outputs[?connector==\`DSI-2\`].transform"# From saved YAML files
redeploy blueprint show blueprint.yaml --query "hardware.drm_outputs[0].transform"
redeploy device-map --show device-map.yaml --query "tags"# JSON output
redeploy hardware pi@192.168.188.109 --query "tags" --format json

JMESPath features:

  • Simple paths:kernel, host, board
  • Array indexing:drm_outputs[0].transform
  • Filtering:[?name==\11-0045`]`
  • Projections:backlights[0].[name,brightness]
  • Wildcards:drm_outputs[*].transform

Supported commands:

  • redeploy hardware --query EXPR
  • redeploy device-map --query EXPR
  • redeploy blueprint show FILE --query EXPR

CLI reference

redeploy run SPEC [options]

Execute deploy from a YAML spec file (or redeploy.yaml project manifest if no arg).

OptionDescription
--plan-onlyShow steps without connecting via SSH
--dry-runConnect, show steps, make no changes
--detectLive-probe host before planning (recommended for prod)
--env NAMEUse named environment from redeploy.yaml (e.g. prod, rpi5)
--plan-out FILESave generated plan to file
redeploy run --env prod # use prod env from redeploy.yaml
redeploy run --env rpi5 --detect # deploy to rpi5 with live probe
redeploy run --dry-run # uses .env DEPLOY_* vars if no redeploy.yaml

redeploy gh-workflow [list|analyze|run]

Analyze and trigger GitHub Actions workflows from your repo on demand.

Prerequisites:

  • GitHub CLI installed: gh
  • Authenticated session: gh auth login
  • Workflow must define workflow_dispatch under on: to be runnable manually

Common usage:

# List all workflow files and dispatch readiness
redeploy gh-workflow list
# Analyze one workflow (triggers/jobs + hint if not dispatchable)
redeploy gh-workflow analyze version-drift
# Analyze all workflows in a custom repo path
redeploy gh-workflow analyze --repo-root /path/to/repo
# Trigger workflow_dispatch run on demand
redeploy gh-workflow run version-drift --ref main
# Pass workflow inputs (repeat --field)
redeploy gh-workflow run release --field env=prod --field force=true
# Trigger and wait for completion (non-zero exit when workflow fails)
redeploy gh-workflow run version-drift --watch
# Preview gh command without executing
redeploy gh-workflow run version-drift --dry-run

Notes:

  • redeploy workflow ... is for workflows from redeploy.css.
  • redeploy gh-workflow ... is for GitHub Actions in .github/workflows/.

Generic pipeline hooks (recommended)

Use top-level hooks: in your migration spec to run custom actions in specific phases.

Supported phases:

  • before_apply
  • before_step
  • after_step
  • on_step_failure
  • on_step_retry
  • after_apply
  • on_failure
  • always

Minimal example:

hooks:
- id: refresh_cachephase: after_applyaction: local_cmdcommand: "curl -fsS -X POST http://localhost:8100/api/v3/cache/clear || true"on_failure: warn
- id: open_browserphase: after_applyaction: open_urlurl: http://localhost:8100/on_failure: warn
- id: before_sync_env_notephase: before_stepwhen: "step.id == 'sync_env'"action: local_cmdcommand: "echo '[hook] about to run sync_env'"on_failure: continue

Notes:

  • when: currently supports simple conditions like step.id == 'sync_env' and step.id != 'sync_env'.
  • Legacy post_deploy/pre_deploy blocks are still accepted and auto-migrated internally.
  • New specs should use hooks: only.

redeploy scan [options]

Discover SSH-accessible devices on the local network.

SourceNetwork activityRequires
known_hostsnone~/.ssh/known_hosts
arpnoneip neigh / arp -a
mdnspassive listenavahi-browse
ping_sweepICMP — active--ping flag

All SSH-reachable devices are saved to registry. Existing entries updated (last_seen, mac, hostname). Old entries never deleted.

redeploy target DEVICE_ID [SPEC] [options]

Deploy a spec to a registered device. Device's host, strategy, app, domain are overlaid onto the spec.

redeploy target pi@192.168.1.42 # uses migration.yaml in cwd
redeploy target pi@192.168.1.42 custom.yaml --dry-run
redeploy target prod-vps --detect --plan-only

After successful deploy, a DeployRecord is saved to the device in registry (timestamp, strategy, version, ok/fail).

redeploy detect / plan / apply / migrate / init / status

redeploy detect --host root@VPS_IP --app myapp -o infra.yaml
redeploy plan --infra infra.yaml --target target.yaml -o plan.yaml
redeploy apply --plan plan.yaml
redeploy migrate --host root@VPS_IP --app myapp --target target.yaml # all in one
redeploy init # scaffold migration.yaml + redeploy.yaml
redeploy status # show project manifest summary

Deployment strategies

StrategyDescriptionUse case
docker_fullDocker Compose — build + upVPS production
podman_quadletRootless Podman systemd unitsQuadlet/rootless VPS
native_kiosksystemd + Chromium OpenboxRPi kiosk (no Docker)
docker_kioskPodman Quadlet in kiosk modeRPi kiosk with container
k3sKubernetes/k3sK3s cluster
systemdNative systemd serviceBare metal

native_kiosk plan steps

Generated automatically when strategy: native_kiosk:

rsync_build → sync build/ to device
run_kiosk_installer → bash build/infra/install-kiosk.sh
install_kiosk_service → scp kiosk.service → /etc/systemd/system/
enable_kiosk_service → systemctl enable --now
wait_kiosk_start → 20s
http_health_check → curl http://localhost:8080

docker_kiosk plan steps

rsync_build → sync build/ to device
install_kiosk_quadlet → cp *.container → ~/.config/containers/systemd/ + daemon-reload
start_kiosk_container → systemctl --user restart app.service
wait_kiosk_start → 20s
http_health_check → curl http://localhost:8080

podman_quadlet plan steps

sync_env → scp .env to remote
install_quadlet_files → cp *.container *.network *.volume → ~/.config/containers/systemd/
podman_daemon_reload → systemctl --user daemon-reload
stop_<app> → systemctl --user stop <app>.service
start_<app> → systemctl --user start <app>.service
wait_startup → 15s
http_health_check → verify_url health endpoint
version_check → verify_version match

For system (root) mode, set stop_services: true in target — switches to systemctl (no --user) and /etc/containers/systemd/.

docker_full plan steps

sync_env → scp env_file → remote_dir/.env
docker_build_pull → docker compose build (on remote)
docker_compose_up → docker compose up -d --build
wait_startup → 30s
http_health_check → verify_url health endpoint
version_check → verify_version match

migration.yaml spec format

name: "myapp deploy 1.0.19 → 1.0.20"description: "Production VPS version bump"source:
strategy: docker_full # docker_full | podman_quadlet | native_kiosk | docker_kiosk | k3s | systemdhost: root@87.106.87.183 # SSH target (user@ip) or "local"app: myappversion: "1.0.19"domain: myapp.example.comremote_dir: ~/myapptarget:
strategy: docker_fullhost: root@87.106.87.183app: myappversion: "1.0.20"domain: myapp.example.comremote_dir: ~/myappcompose_files:
- docker-compose.vps.ymlenv_file: envs/vps.envverify_url: https://myapp.example.com/api/v1/healthverify_version: "1.0.20"extra_steps: # optional — appended or inserted
- id: flush_k3s_iptables # StepLibrary name — no action neededinsert_before: docker_build_pull # inject before specific step
- id: docker_prune # StepLibrary: prune unused images
- id: notify_slack # custom step (needs action:)action: ssh_cmddescription: "Send deploy notification"command: "curl -s -X POST $SLACK_WEBHOOK -d '{\"text\":\"deployed 1.0.20\"}'"risk: low

StepLibrary — reusable named steps

Reference any step by id alone — no action needed. Fields can be overridden:

extra_steps:
- id: flush_k3s_iptables # use as-is
- id: stop_k3s
- id: http_health_checkurl: https://myapp.example.com/health # override url
- id: wait_startup_long # 60s instead of 30s
IDActionDescription
flush_k3s_iptablesssh_cmdFlush CNI-HOSTPORT-DNAT + KUBE-* chains (stale k3s rules block Docker-proxy on 80/443)
delete_k3s_ingresseskubectl_deleteDelete all k3s ingresses
stop_k3ssystemctl_stopStop k3s service
disable_k3ssystemctl_disableDisable k3s on boot
stop_nginxsystemctl_stopStop host nginx (port 80 conflict)
restart_traefikssh_cmdRestart Traefik container
docker_prunessh_cmdPrune unused images + build cache
docker_compose_downdocker_compose_downStop Docker Compose stack
wait_startupwaitWait 30s
wait_startup_longwaitWait 60s
http_health_checkhttp_checkVerify health endpoint (expect: healthy)
version_checkversion_checkVerify deployed version
sync_envscpCopy .env to remote
podman_daemon_reloadsystemctl_startsystemctl --user daemon-reload
stop_podmansystemctl_stopStop all Podman containers via systemd
enable_podman_unitsystemctl_startsystemctl daemon-reload && enable --now {service}.service
systemctl_restartsystemctl_startRestart a systemd service (command= to override)
systemctl_daemon_reloadssh_cmdsystemctl daemon-reload
git_pullssh_cmdgit pull --ff-only with rollback (git reset --hard HEAD@{1})

insert_before

By default extra steps are appended after all generated steps. Use insert_before: <step_id> to inject at a specific position:

extra_steps:
- id: flush_k3s_iptablesinsert_before: docker_build_pull # runs before build, not after verify

Query-language post-deploy tests — testql / oql / aql

Beyond http_check/inline_script, redeploy can run query-language test scenarios as post-deploy verification. Each runs the interpreter locally (on the controller, where it is installed) and targets the freshly deployed host via url. The step fails — and thus the deploy fails/rolls back — when the scenario fails.

ActionDefault runnerVerdictPurpose
testqltestql run --url <url> <file>exit 0 (+ optional expect in output)API/GUI smoke of the deployed app
oqloqlctl <file> -m <mode> --json [--firmware-url <url>]JSON ok == true (fallback: exit 0)run an OQL scenario against the deployed runtime/firmware
aqlaql <file> [locale]exit 0 (+ optional expect, e.g. a variant name)evaluate an AQL decision model and assert the verdict

Fields (on top of url / expect):

  • query_source — path to the .testql.toon.yaml / .oql / .aql scenario.
  • query_mode — OQL mode: validate | dry-run | execute.
  • query_runner — override the runner binary (absolute path is fine).
  • query_locale — AQL locale (en | pl).
  • query_context — AQL: path to a context JSON for resolve.
  • command — full command-template override; placeholders {url} {source} {mode} {locale} {context} are substituted (literal {} in the command are left intact — no str.format).
extra_steps:
- id: post_deploy_oqlaction: oqldescription: "OQL: real sensor read from the deployed firmware"query_source: deploy-tests/oql/firmware-sensor-read.oqlquery_mode: executeurl: http://192.168.188.122:8202 # oqlctl --firmware-url
- id: post_deploy_testqlaction: testqldescription: "TestQL: API smoke of the deployed app"query_source: deploy-tests/testql/post-deploy-api-smoke.testql.toon.yamlflags: ["--output", "json", "--quiet"]url: http://192.168.188.109:8100 # testql run --url
- id: post_deploy_aqlaction: aqldescription: "AQL: decision model returns the expected variant"query_source: deploy-tests/aql/personalization.en.aqlcommand: "python3 /path/to/aql/cli.py {source} {locale}"query_locale: enexpect: technical_deep

A ready example spec lives in c2004: deploy-tests/post-deploy.redeploy.md (run with redeploy run deploy-tests/post-deploy.redeploy.md).

Plugin system

Extend the step pipeline with custom action types using action: plugin:

extra_steps:
- id: reload_kioskaction: pluginplugin_type: browser_reloaddescription: Reload kiosk browser after deployplugin_params:
port: 9222ignore_cache: trueurl_contains: "localhost:8100"

Built-in plugins

plugin_typeDescriptionplugin_params
browser_reloadReload Chromium via CDP (Chrome DevTools Protocol) over SSHport (9222), ignore_cache (true), url_contains ("")

Writing a custom plugin

Place a .py file in ./redeploy_plugins/ (project-local) or ~/.redeploy/plugins/ (user-global):

# ./redeploy_plugins/notify.pyfromredeploy.pluginsimportregister_plugin, PluginContextfromredeploy.modelsimportStepStatus@register_plugin("notify_slack")defnotify_slack(ctx: PluginContext) ->None:
webhook=ctx.params["webhook"]
ctx.probe.run(f"curl -X POST {webhook} -d '{{\"text\":\"deployed!\"}}'")
ctx.step.result="notified"ctx.step.status=StepStatus.DONE

PluginContext fields:

FieldTypeDescription
stepMigrationStepCurrent step — set result and status here
hoststrSSH host (e.g. pi@192.168.1.5)
probeRemoteProbeCall probe.run(cmd) for remote SSH commands
emitterProgressEmitter?Emit mid-step progress: emitter.progress(step.id, msg)
paramsdictShortcut for step.plugin_params
dry_runboolSkip side-effects if True

Inline Scripts

Execute multiline bash scripts directly from YAML without external files:

extra_steps:
- id: configure_kioskaction: inline_scriptdescription: "Deploy kiosk launch script"command: | #!/bin/bash mkdir -p ~/c2004/config cat > ~/c2004/config/kiosk-launch.sh << 'EOF' #!/bin/bash if command -v chromium-browser >/dev/null 2>&1; then chromium-browser --kiosk http://localhost:8100 elif command -v firefox >/dev/null 2>&1; then firefox --kiosk http://localhost:8100 fi EOF chmod +x ~/c2004/config/kiosk-launch.shrisk: mediumtimeout: 60

The script is base64-encoded and executed via SSH with automatic temp file cleanup. Use command field for multiline script content (YAML | preserves newlines).

Script References (command_ref)

Instead of duplicating scripts in YAML, reference a script defined in a markdown codeblock:

extra_steps:
- id: configure_kioskaction: inline_scriptdescription: "Execute kiosk script from markdown"command_ref: "#kiosk-browser-configuration-script"risk: medium

In your migration markdown file, define the script in a section:

## Kiosk Browser Configuration Script```bash#!/bin/bash# Auto-detect browser...ifcommand -v chromium-browser >/dev/null 2>&1;then
chromium-browser --kiosk http://localhost:8100
fi

**Benefits:**
- Single source of truth — script lives in one place (markdown codeblock)
- No duplication between markdown documentation and YAML
- Easy to read and maintain
- Changes to the codeblock automatically apply to the deployment
**Reference formats:**
- `"#section-id"` — script from section in current spec file
- `"./file.md#section-id"` — script from section in specific file
The section ID is derived from the heading: spaces become hyphens, lowercase.
Example: `## Kiosk Browser Configuration Script` → `#kiosk-browser-configuration-script`
### Execute Script by Reference (`redeploy exec`)
Run a single script from markdown without running the full migration:
```bash
# Execute script from codeblock on remote host
redeploy exec '#kiosk-browser-configuration-script' \
--host pi@192.168.188.108 \
--file migration.podman-rpi5-resume.md
# With file in reference
redeploy exec './migration.md#install-deps' --host root@server.com
# Using markpact:ref (more explicit)
redeploy exec 'kiosk-script-id' --host pi@192.168.188.108 --file migration.md
# Dry-run to preview script
redeploy exec '#backup-script' --host pi@192.168.188.108 --file ops.md --dry-run

This is useful for:

  • One-off operations defined in markdown docs
  • Testing individual scripts before full migration
  • Running maintenance tasks

Execute Multiple Scripts (redeploy exec-multi)

Test multiple scripts at once:

# Execute multiple scripts by ref
redeploy exec-multi 'kiosk-script,install-deps,cleanup' \
--host pi@192.168.188.108 \
--file migration.md
# Mix of markpact:ref and section headings
redeploy exec-multi 'script1,#section2,script3' \
--host root@server.com \
--file deploy.md \
--dry-run

Marking Codeblocks with markpact:ref

For more explicit script identification, use markpact:ref <id> in codeblock:

```bash markpact:ref kiosk-browser-configuration-script
#!/bin/bash# Auto-detect browser...ifcommand -v chromium-browser >/dev/null 2>&1;then
chromium-browser --kiosk http://localhost:8100
fi

Benefits of `markpact:ref`:
- Explicit ID assignment (not derived from heading)
- Multiple scripts per section
- Can reference by simple ID instead of full heading
- Self-documenting in markdown
Place in project root — `redeploy run` (no args) uses it automatically.
Supports **named environments** for multi-target projects:
```yaml
spec: migration.yaml # default spec file
app: myapp
environments:
prod:
host: root@87.106.87.183
strategy: docker_full
domain: myapp.example.com
env_file: envs/vps.env
verify_url: https://myapp.example.com/api/v1/health
rpi5:
host: pi@192.168.188.108
strategy: systemd
env_file: .env
verify_url: http://192.168.188.108:8000/api/v1/health
dev:
host: local
strategy: docker_full
env_file: .env.local
verify_url: http://localhost:8000/api/v1/health

Fallback: if no redeploy.yaml found, redeploy run reads DEPLOY_* vars from .env:

# .env
DEPLOY_HOST=pi@192.168.1.5
DEPLOY_APP=myapp
DEPLOY_DOMAIN=myapp.local
DEPLOY_ENV_FILE=.env

doql integration

redeploy is the deploy engine for doql declarative apps.

# Install with doql integration
pip install doql[deploy]
# doql build generates build/infra/migration.yaml automatically
DEPLOY_HOST=root@YOUR_VPS doql build
# Then deploy — no args needed
doql deploy # calls redeploy API internally
doql deploy --plan-only
doql deploy --dry-run
doql quadlet --install # installs Quadlet units via redeploy

doql DEPLOY.target → redeploy strategy mapping:

doqlredeploy
docker-composedocker_full
quadletpodman_quadlet
kiosk-appliancenative_kiosk
kubernetesk3s

IaC/CI config coverage (via redeploy import, used by doql/redeploy workflows):

  • Docker Compose + Dockerfile
  • nginx configs (nginx.conf, *.conf)
  • Kubernetes manifests (apiVersion + kind YAML)
  • Terraform (*.tf, *.tfvars)
  • TOML (pyproject.toml, app/tool TOML)
  • Vite config (vite.config.ts/js/mjs/cjs)
  • CI/CD: GitHub Actions, GitLab CI, Jenkinsfile

Parser plugin extension:

  • Python entry points: redeploy.iac.parsers
  • Project-local parsers: ./redeploy_iac_parsers/*.py
  • User-global parsers: ~/.redeploy/iac_parsers/*.py

Built-in template generator:

  • redeploy import --list-plugin-templates
  • redeploy import --plugin-template helm-kustomize
  • redeploy import --plugin-template argocd-flux --plugin-dir redeploy_iac_parsers

Example external plugin (Helm + Ansible):

  • Source template: examples/redeploy_iac_parsers/helm_ansible.py
  • Quick start:
    1. mkdir -p redeploy_iac_parsers
    2. cp examples/redeploy_iac_parsers/helm_ansible.py redeploy_iac_parsers/
    3. redeploy import path/to/Chart.yaml
    4. redeploy import path/to/playbook.yml

Example external plugin (Helm templates + Kustomize):

  • Source template: examples/redeploy_iac_parsers/helm_kustomize.py
  • Quick start:
    1. mkdir -p redeploy_iac_parsers
    2. cp examples/redeploy_iac_parsers/helm_kustomize.py redeploy_iac_parsers/
    3. redeploy import path/to/chart/templates/deployment.yaml
    4. redeploy import path/to/kustomization.yaml

Example external plugin (ArgoCD Application + Flux Kustomization):

  • Source template: examples/redeploy_iac_parsers/argocd_flux.py
  • Quick start:
    1. mkdir -p redeploy_iac_parsers
    2. cp examples/redeploy_iac_parsers/argocd_flux.py redeploy_iac_parsers/
    3. redeploy import path/to/argocd-application.yaml
    4. redeploy import path/to/flux-kustomization.yaml

Example external plugin (GitOps CI for ArgoCD/Flux):

  • Source template: examples/redeploy_iac_parsers/gitops_ci.py
  • Quick start:
    1. mkdir -p redeploy_iac_parsers
    2. cp examples/redeploy_iac_parsers/gitops_ci.py redeploy_iac_parsers/
    3. redeploy import .github/workflows/deploy-gitops.yml
    4. redeploy import .gitlab-ci.yml

Examples

DirectoryScenarioStrategy
01-vps-version-bumpVPS Docker version bumpdocker_full → docker_full
02-k3s-to-dockerMigrate off k3sk3s → docker_full
03-docker-to-podman-quadletMove to rootless Podmandocker_full → podman_quadlet
04-rpi-kioskRaspberry Pi kiosk updatenative_kiosk → native_kiosk
05-iot-fleet-otaIoT fleet OTA updatedocker_full → docker_full
09-fleet-yamlFleet with stages + scanfleet + redeploy target
11-traefik-tlsTraefik + Let's Encryptdocker_full → podman_quadlet
12-ci-pipelineGitHub Actions / GitLab CICI-triggered docker_full
# Run any example in dry-run mode (no SSH required):
redeploy run examples/01-vps-version-bump/migration.yaml --plan-only
redeploy run examples/04-rpi-kiosk/migration.yaml --plan-only

Self-healing deploy — redeploy fix

redeploy fix is the recommended day-to-day deploy command. It:

  1. bumps the patch version in VERSION + spec header
  2. applies the migration spec with --heal enabled
  3. if a step fails, calls an LLM (via LiteLLM / OpenRouter) to suggest a fix and retries automatically
# Self-healing deploy: bump version → run → LLM retry on failure
redeploy fix .
redeploy fix redeploy/pi109/migration.md
# With a problem hint for the LLM
redeploy fix . --hint "service not starting after update"
redeploy fix . --hint "brak ikon SVG w menu"# Preview only (no apply)
redeploy fix . --dry-run
# Bump minor version instead of patch
redeploy fix . --minor
# Bump major version
redeploy fix . --major
redeploy fix . --retries 5
# Skip version bump
redeploy fix . --no-bump

Spec discovery from .:

  • ./migration.md or ./migration.yaml — direct match
  • ./redeploy/<target>/migration.md — project pattern (lists targets, asks if multiple)
  • Recursive fallback anywhere under the directory

redeploy fix automatically discovers migration specs — running from project root with multiple targets prompts interactively.

Version management — redeploy bump

# Bump patch (default): 1.0.31 → 1.0.32
redeploy bump .
redeploy bump redeploy/pi109/migration.md
# Bump minor: 1.0.31 → 1.1.0
redeploy bump . --minor
# Bump major: 1.0.31 → 2.0.0
redeploy bump . --major

Updates VERSION file and all version references in the migration spec (version:, name: "... vX.Y.Z", description: "... vX.Y.Z").

LLM self-healing on redeploy run

redeploy run also supports --heal mode (enabled by default):

# Run with LLM self-healing (default)
redeploy run migration.yaml
# Disable healing
redeploy run migration.yaml --no-heal
# Pass problem description to LLM
redeploy run migration.yaml --fix "nginx port conflict"# Max heal retries
redeploy run migration.yaml --max-heal-retries 5

LLM reads the failed step output, runs SSH diagnostics, and patches the spec YAML. Repairs are logged to REPAIR_LOG.md next to the spec.

Requires OPENROUTER_API_KEY (or OPENAI_API_KEY) in .env or ~/.redeploy/.env. Model defaults to openrouter/qwen/qwen3-coder-next (override with LLM_MODEL=...).


Natural language interface — redeploy prompt

# Map a natural language instruction to a redeploy command
redeploy prompt "deploy c2004 to pi109"
redeploy prompt "pokaż plan deployu na pi109"
redeploy prompt "bump version and redeploy" --yes
redeploy prompt "what specs are available?" --schema-only
# Force dry-run on generated command
redeploy prompt "run the pi109 migration" --dry-run
# Skip confirmation
redeploy prompt "fix the frontend service" --yes
# Preview the workspace schema sent to the LLM
redeploy prompt "..." --show-schema

The LLM receives a workspace schema (discovered specs, version, git branch, command catalogue) and maps the instruction to a concrete redeploy invocation.

Language is auto-detected — Polish, English, or any language the model supports.


MCP server — redeploy mcp

redeploy exposes an MCP (Model Context Protocol) server, letting AI assistants (Claude Desktop, VS Code Copilot, custom agents) call redeploy operations as structured tools.

Start

# stdio — for Claude Desktop / VS Code local integration
redeploy mcp
# HTTP SSE — for remote/shared access
redeploy mcp --transport sse --port 8811
# Streamable HTTP
redeploy mcp --transport http --port 8811
# Standalone binary (no CLI wrapper)
redeploy-mcp --transport sse

Available MCP tools

ToolDescription
schemaDiscover workspace: specs, version, git branch, command catalogue
list_specsList all migration specs found in a directory
plan_specPreview a spec (dry-run) — safe, no changes
run_specApply a migration spec
fix_specSelf-healing deploy: bump → apply → LLM retry
bump_versionBump patch/minor/major version
diagnoseSSH diagnostics on a remote host
exec_sshRun an ad-hoc command on a remote host
nlp_commandTranslate NLP instruction → redeploy command

MCP resources

URIDescription
redeploy://workspaceCurrent workspace schema as JSON
redeploy://spec/{path}Raw content of a migration spec file

Claude Desktop integration

Add to ~/.config/claude/claude_desktop_config.json:

{
"mcpServers": {
"redeploy": {
"command": "redeploy",
"args": ["mcp"]
}
}
}

VS Code Copilot integration

Add to .vscode/mcp.json in your workspace:

{
"servers": {
"redeploy": {
"type": "stdio",
"command": "redeploy",
"args": ["mcp"]
}
}
}

Or for an SSE server already running on a remote machine:

{
"servers": {
"redeploy-remote": {
"type": "sse",
"url": "http://192.168.188.109:8811/sse"
}
}
}

Install with MCP dependencies

pip install "redeploy[mcp]"

Hardware diagnostics commands

redeploy hardware HOST [options]

Probe and diagnose hardware on a remote host (DSI display, DRM connectors, backlight, I2C, config.txt, Wayland compositor).

OptionDescription
`--format [yamljson]`
--fixPrint fix commands for all issues found
--apply-fix COMPONENTRun fix for specific component via SSH
--panel PANEL_IDSpecify panel ID explicitly
--list-panelsList available panel definitions
--set-transform TRANSFORMSet display rotation for DSI output (normal, 90, 180, 270, flipped, etc.)
--apply-config FILEApply display settings from YAML/JSON config file
--query EXPRExtract specific values using JMESPath query
--ssh-key PATHSSH private key path
redeploy hardware pi@192.168.188.109
redeploy hardware pi@192.168.188.109 --fix
redeploy hardware pi@192.168.188.109 --set-transform 270
redeploy hardware pi@192.168.188.109 --apply-config hardware.yaml
redeploy hardware pi@192.168.188.109 --query "drm_outputs[0].transform"

redeploy device-map HOST [options]

Generate full device snapshot (hardware + infra + diagnostics).

OptionDescription
--name TEXTHuman-friendly device label
--tag TEXTTag(s) to attach (repeatable)
--savePersist map to ~/.config/redeploy/device-maps/
--out PATHSave to specific file
`--format [yamljson]`
--no-infraSkip infra probe (hardware only)
--listList saved device maps
--show PATHLoad and display saved device-map file
--diff PATH...Diff two saved device-map files
--apply-config FILEApply hardware/infra settings from YAML config file
--query EXPRExtract specific values using JMESPath query
--ssh-key PATHSSH private key path
redeploy device-map pi@192.168.188.109 --save --name "kiosk-lab"
redeploy device-map --list
redeploy device-map --show ~/.config/redeploy/device-maps/pi_at_192.168.188.109.yaml
redeploy device-map pi@192.168.188.109 --apply-config device-map.yaml
redeploy device-map pi@192.168.188.109 --query "hardware.drm_outputs[0].transform"

redeploy blueprint [command]

Manage device blueprints (capture, show, list, twin, migrate).

redeploy blueprint capture HOST [options]

Capture device state as blueprint.

OptionDescription
`--format [yamljson]`
--savePersist to ~/.config/redeploy/blueprints/
--out PATHSave to specific file
--ssh-key PATHSSH private key path

redeploy blueprint show FILE [options]

Display saved blueprint.

OptionDescription
`--format [yamljson]`
--apply-config FILEApply blueprint settings from YAML config file
--query EXPRExtract specific values using JMESPath query
redeploy blueprint capture pi@192.168.188.109 > blueprint.yaml
redeploy blueprint show blueprint.yaml --apply-config blueprint.yaml
redeploy blueprint show blueprint.yaml --query "hardware.drm_outputs[0].transform"

Dependencies

Core runtime dependencies:

PackagePurpose
pydantic>=2.0Data validation and settings
pyyaml>=6.0YAML parsing/serialization
markdown-it-py>=3.0Markdown parsing (markpact specs)
click>=8.0CLI framework
loguru>=0.7Structured logging
paramiko>=3.0SSH client
httpx>=0.25HTTP client
rich>=13.0Terminal UI
jmespath>=1.0JSON/YAML query expressions
goal>=2.1.0Goal tracking
costs>=0.1.20AI cost tracking
pfix>=0.1.60Self-healing Python

Optional dependencies:

PackagePurpose
op3>=0.1.8OP3 support
mcp>=1.0MCP server mode

Internal Modules

  • markpact — Markdown-native deployment spec format (markpact:config, markpact:steps)
  • goal — Migration goal tracking and validation
  • costs — AI cost tracking and reporting

License

Licensed under Apache-2.0.

About

Infrastructure migration toolkit: detect → plan → apply

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages