English · فارسی
Generic deploy toolkit for Chabokan PaaS — stage/package → FTP (manifest) → content-only restart → Hub logs → HTTP health. English-only CLI. Project names, Hub services, and FTP targets come from your
chabokan.project.yaml+ env — nothing customer-specific is hard-coded.
Quick links: Quick Start · Configure · Commands · False-restart defense · Architecture · Docs · فارسی · Contributing · Security · License
chabokan-ctl is a production-grade Python CLI and optional Tk menu for teams deploying to Chabokan (Iranian PaaS). It wraps the repetitive, error-prone parts of FTP sync, Hub API calls, log analysis, billing visibility, and health checks into one Rich-powered workflow.
stage/package → FTP (manifest) → content-only restart → Hub logs → HTTP health
Built for developers who need repeatable ships, false-restart defense, and honest FTP verify — not another bash script that restarts on every metadata touch.
Created by Ali Rashidi. MIT-licensed generic toolkit hardened from production Chabokan deploy workflows.
- Why chabokan-ctl
- Features
- Architecture
- Ship pipeline
- Repository layout
- Prerequisites
- Quick start
- Install
- Configure
- Commands
- False-restart defense
- Testing
- Documentation map
- FAQ
- Troubleshooting
- Security
- Contributing
- License
- Contact
| Pain on Chabokan | What chabokan-ctl does |
|---|---|
| Manual FTP + panel restarts | chabokan ship all — staged upload, verify, conditional restart |
Restarts on PACKAGE.txt timestamp changes |
Stable fingerprint manifest + content-only restart policy |
| “0 uploaded / N skipped” but stale remote | RETR-MD5 verify; manifest is a hint, not skip authority |
| Orphan purge deleting uploads/logs | Path normalization + mass-delete guard (≥50% abort) |
| Hub logs vanish after restart | chabokan file-logs pulls persistent FTP logs/ |
| Opaque deploy failures | Pre-deploy log audit, post-deploy issue table, doctor |
| Windows Persian terminal garble | Optional rtl-probe; English product UI by default |
| Area | Capabilities |
|---|---|
| Ship / deploy | Stage tree, parallel MD5, FTP upload/skip, verify, restart policy, /api/ready gate, safe web skip |
| Hub API | Service logs, restart, credentials fetch (redacted by default), billing/cost |
| FTP | MLSD index, parallel RETR, orphan purge with safety guards, preserve prefixes |
| Logs | Pre-deploy audit, post-deploy analysis, persistent file-logs pull |
| Health | Stack-aware probes (python-fastapi, nginx-static, nextjs, …) |
| CLI UX | Typer + rich-click, --plain / --json, usage-assist for common mistakes |
| Menu / GUI | chabokan menu (Tk on Windows), result dialogs after doctor/ship |
| Safety | Atomic safety matrix tests, contract tests for PACKAGE.txt stability |
| i18n lab | rtl-probe for Persian terminal display experiments (not product UI) |
High-level module map (see docs/architecture.md):
cli/ Typer + rich-click entrypoint
core/ config, credentials, API client, project YAML
ftp/ FTP client + restart_policy
ship/ pipeline, staging
logs/ build_state, analysis, display
ui/ console, theme, stylekit, RTL, menu
health/ parallel HTTP probes
billing/ wallet / cost helpers
platform/ stack profiles
flowchart TB
subgraph cli [CLI layer]
TY[Typer / rich-click]
MENU[Tk menu optional]
end
subgraph core [Core]
CFG[chabokan.project.yaml]
ENV[.env.chabokan secrets]
API[Hub API client]
end
subgraph ship [Ship pipeline]
STG[Stage / package]
FTP[FTP sync + verify]
RST[Restart policy]
HLTH[Health probes]
end
subgraph obs [Observability]
LOGS[Log analysis]
DOC[Doctor / monitor]
BILL[Billing tables]
end
TY --> CFG
TY --> ENV
TY --> STG
STG --> FTP
FTP --> RST
RST --> API
API --> LOGS
LOGS --> DOC
API --> BILL
RST --> HLTH
MENU --> TY
REPO_ROOT is detected by walking parents for chabokan.project.yaml (or chabokan.project.example.yaml). Works from application repos, monorepo packages/ layouts, or this standalone checkout.
End-to-end flow for chabokan ship all:
sequenceDiagram
participant Dev as Developer
participant CTL as chabokan-ctl
participant Stage as Local stage
participant FTP as Chabokan FTP
participant Hub as Chabokan Hub API
participant App as Running service
Dev->>CTL: chabokan ship all
CTL->>Stage: stage_service (package tree)
CTL->>FTP: classify + upload changed files
CTL->>FTP: RETR-MD5 verify (skip authority)
alt content changed
CTL->>Hub: restart service
CTL->>Hub: tail build/runtime logs
CTL->>App: HTTP health wait
else metadata only
CTL-->>Dev: Restart skipped — metadata only
end
CTL-->>Dev: Deploy recap + cost/wallet (optional)
Three-layer PACKAGE.txt defense (see docs/ftp-and-restart.md):
- Stable content —
Content-Fingerprintfrom sorted path+size; noGenerated:timestamp. - UTF-8 no BOM — PowerShell must not write
EF BB BF. - Skip + content restart — default
ftp_skip_pathsincludesPACKAGE.txt; metadata basenames ignored for restart decisions.
When API and frontend are separate Chabokan services:
chabokan ship api → GET /api/ready = 200 → chabokan ship web
web_ship_blocked_by_api() skips web in the same batch if API deploy failed or
/api/ready did not pass (api_deploy_failed / api_health_not_ready).
/api/health is stealth ops info only — not a ship gate.
Ops runbook: docs/ops/deploy-recovery-runbook.md · FA
chabokan-ctl/
├── src/chabokan_ctl/ # Python package (59 modules)
├── tests/ # 287 unit/contract tests (+ 2 opt-in live)
├── docs/ # MkDocs + README map
├── scripts/ # chabokan.ps1 Windows helper
├── chabokan.project.yaml # Dev/test profile (generic myapp)
├── chabokan.project.example.yaml
├── pyproject.toml # Hatchling, chabokan entry point
├── mkdocs.yml
└── .github/workflows/ # CI (pytest, ruff, build)
| Requirement | Notes |
|---|---|
| Python 3.11+ | 3.12 supported in CI matrix |
| Chabokan Hub account | API token + per-service FTP credentials |
| Application repo | With deployable tree (Docker / static / FastAPI, etc.) |
| Windows (optional) | Tk menu + scripts/chabokan.ps1 UTF-8 helper |
git clone https://github.com/Ali-Rashidi-80/chabokan-ctl.git
cd chabokan-ctl
pip install -e ".[dev]"
chabokan --helpWire your app repo:
cp chabokan.project.example.yaml /path/to/your-app/chabokan.project.yaml
# edit services, URLs, FTP roots
cp .env.chabokan.example /path/to/your-app/.env.chabokan
# fill CHABOKAN_* secrets (gitignore .env.chabokan)
cd /path/to/your-app
chabokan preflight all
chabokan ship allpip install -e .
# or from PyPI (when published):
# pip install chabokan-ctl
chabokan --helpWindows helper (optional, sets UTF-8):
.\scripts\chabokan.ps1 --help- Copy
chabokan.project.example.yamltochabokan.project.yamlat your application repo root. For every Chabokan stack type (Python, Node, PHP, DB, MinIO, …) see the full referencechabokan.project.yamlin this repo. - Create
.env.chabokan(gitignored) withCHABOKAN_API_TOKENand FTP vars. - Run
chabokan preflight all.
Example env keys (see .env.chabokan.example):
CHABOKAN_API_TOKEN=...
CHABOKAN_API_FTP_HOST=...
CHABOKAN_API_FTP_USER=...
CHABOKAN_API_FTP_PASS=...
CHABOKAN_WEB_FTP_HOST=...
CHABOKAN_WEB_FTP_USER=...
CHABOKAN_WEB_FTP_PASS=...Credentials fetch (redacted by default):
chabokan credentials fetch my-wp-service
chabokan credentials fetch my-wp-service --show-secrets # intentional reveal only| Command | Purpose |
|---|---|
chabokan menu |
Interactive help |
chabokan ship all |
Full pipeline |
chabokan doctor api |
Logs + HTTP |
chabokan file-logs api |
FTP persistent logs |
chabokan monitor |
Cost + health |
chabokan rtl-probe |
Optional terminal RTL lab |
Extended groups (see docs/commands.md):
| Group | Examples |
|---|---|
| menu (help) | chabokan menu, chabokan menu ship, chabokan menu --no-gui |
| ship / deploy | chabokan ship all, chabokan deploy web --wait |
| preflight / audit | chabokan preflight all, chabokan predeploy-audit |
| service | chabokan service logs api --tail 80, chabokan services list |
| credentials | chabokan credentials fetch wp |
| ftp | chabokan ftp test web, chabokan ftp sync api |
| doctor | chabokan doctor all, chabokan doctor api --source both |
| cost / monitor | chabokan cost, chabokan monitor |
| package | chabokan package web --no-zip |
Global flags: --plain, --rtl auto|shaped|visual|on|off, --json, --gui/--no-gui, --env-file, --verbose.
Stable PACKAGE.txt (no timestamp), UTF-8 without BOM, restart only when
content paths change. Metadata-only syncs print:
Restart skipped — metadata only
No Restarting … line when only metadata would upload.
When FTP content is unchanged but you need a Docker rebuild:
chabokan ship api --force-restartFull policy: docs/ftp-and-restart.md.
pip install -e ".[dev]"
pytest -q
ruff check src tests
black --check src tests
isort --check-only src tests
mypy src/chabokan_ctl
python -m compileall -q src testsWindows one-shot (mirrors CI):
.\scripts\run-qa.ps1| Suite | Count | Notes |
|---|---|---|
| Unit + contract | 287 | Default CI; no network |
| Skipped live | 2 | Opt-in Hub/FTP |
| Live Hub | opt-in | CHABOKAN_LIVE_TESTS=1 — use a non-production project only |
Smoke commands (from ACCEPTANCE.md):
chabokan --help
chabokan menu --no-gui
chabokan rtl-probe --json-out
| Document | Description |
|---|---|
| docs/README.md | Documentation index |
| docs/getting-started.md | First-time setup |
| docs/architecture.md | Module map |
| docs/commands.md | Command reference |
| docs/ftp-and-restart.md | FTP verify + restart policy |
| docs/gui-and-menu.md | Tk menu + dialogs |
| docs/i18n-persian.md | RTL probe notes |
| docs/stacks.md | Platform stack profiles |
| docs/ops/deploy-recovery-runbook.md | Split API/web ops (EN) |
| docs/ops/deploy-recovery-runbook.fa.md | راهنمای بازیابی deploy (FA) |
| docs/migration-v2.md | v1 scripts → v2 package |
| CHANGELOG.md | Release history |
| ACCEPTANCE.md | Definition of done |
Build MkDocs site locally:
pip install -e ".[dev]"
mkdocs serveIs this tied to one customer project?
No. v2.0.5+ loads services only from your YAML. No hard-coded tenant or service names.
Why English-only UI?
Mixed FA/EN in Click help columns breaks BiDi box borders on Windows Terminal. Persian remains available via rtl-probe for terminal experiments.
Can I use it from a monorepo?
Yes. Install with pip install -e packages/chabokan-ctl and place chabokan.project.yaml at the monorepo root. See docs/dual-repo.md for optional mirror sync.
Does ship always restart?
Only when FTP content changed (RETR-MD5 proven). Metadata-only uploads skip restart.
| Symptom | Likely cause | Fix |
|---|---|---|
Missing .env.chabokan |
Secrets file not created | Copy .env.chabokan.example, fill values |
Restart skipped — metadata only |
Only PACKAGE/manifest changed | Expected; use --force-restart if image rebuild needed |
| FTP verify failures after purge | Path normalization mismatch (historical) | Upgrade ≥2.0.2; check preserve prefixes |
| Persian garbage in terminal | Wrong RTL strategy | Run chabokan rtl-probe; set CHABOKAN_RTL=logical |
Empty --help rows |
Old rich-click | Upgrade package ≥2.0.4 |
- Never commit
.env.chabokan, production tokens, or FTP passwords. credentials fetchredacts secrets unless--show-secretsis explicit.- Report vulnerabilities per SECURITY.md — do not open public issues with exploit details.
See CONTRIBUTING.md and docs/contributing.md. PRs welcome for bug fixes, docs, and safety improvements. Live ship tests must use a dedicated sandbox project.
MIT — see LICENSE.
- GitHub: Ali-Rashidi-80/chabokan-ctl
- Issues: github.com/Ali-Rashidi-80/chabokan-ctl/issues
- Support: SUPPORT.md
