# 🛠️ DX `dx` is a framework-agnostic bash CLI that orchestrates the full development lifecycle for PHP projects — Docker, Apache, Traefik, environment config, migrations, and tests — across multiple frameworks (Yii2, Laravel). The `dx` script at the project root is the entrypoint. It loads `dxkit/bootstrap.sh` and dispatches to the command and module system inside `dxkit/`. ## Prerequisites - Bash (Linux/macOS native; Windows via Git Bash or WSL) - Docker + Docker Compose - `dx` must be executable: `chmod +x dx` ## Quick start ```bash ./dx setup # first time: build image + init environment ./dx up # start the stack ./dx shell # enter the app container ./dx console migrate # run database migrations ``` ## Command reference ### Setup & initialization | Command | Description | |---|---| | `dx build` | Build the Docker image and generate the compose stack | | `dx init [env]` | Bring the project to a runnable state for an environment (default: `dev`) | | `dx setup` | `build` + `init` in one step | **`dx build` flags** | Flag | Description | |---|---| | `--dev` / `--qly` / `--prd` | Target environment (default: `--dev`) | | `--project=NAME` | Override the project name | | `--port=PORT` | Override the app HTTP port | | `--db=mysql\|mssql` | Database engine (default: `mysql`) | | `--db-port=PORT` | Override the database port | | `--sync-hosts` | Write the project domain to `/etc/hosts` after build | | `--no-vhosts` | Skip Apache virtual host generation | | `--no-interact` | Non-interactive mode (no prompts) | | `--debug` | Enable shell trace output | **`dx init` flags** | Flag | Description | |---|---| | `--skip-vendors` | Skip `composer install` | | `--skip-framework-init` | Skip framework init script | | `--skip-framework-config` | Skip framework config generation | | `--skip-proxy` | Skip starting the reverse proxy | | `--debug` | Enable shell trace output | --- ### Runtime | Command | Description | |---|---| | `dx up` | Start the Docker stack | | `dx down` | Stop the Docker stack | | `dx logs` | Tail container logs | | `dx shell` / `dx bash` | Open an interactive shell inside the app container | | `dx exec [args]` | Run a command inside the app container | | `dx list` / `dx ls` | List stack containers | | `dx test [args]` | Run PHPUnit tests inside the container | **`dx up` flags** | Flag | Description | |---|---| | `--sync-hosts` | Sync domain entries to `/etc/hosts` before starting | | `--skip-proxy` | Do not ensure the reverse proxy is running | | `--recreate` | Force container recreation | | `--build` | Rebuild the image before starting | | `--foreground` | Run in foreground (don't detach) | | `--debug` | Enable shell trace output | **`dx down` flags** | Flag | Description | |---|---| | `--volumes` | Also remove Docker volumes | | `--orphans` | Remove orphan containers | | `--debug` | Enable shell trace output | --- ### Framework console | Command | Description | |---|---| | `dx console [args]` | Run a framework console command | | `dx yii ` | Alias for Yii2 (`php yii `) | | `dx artisan ` | Alias for Laravel (`php artisan `) | --- ### Migration shortcuts These are resolved by the active framework driver. **Yii2** | Command | Description | |---|---| | `dx migrate` | Run all pending migrations | | `dx migrate-create ` | Generate a new timestamped migration file in `console/migrations/` | | `dx migrate-down [n]` | Revert `n` migrations (default: 1) | | `dx migrate-new` | Show pending migrations | | `dx migrate-history` | Show migration history | **Laravel** | Command | Description | |---|---| | `dx migrate` | Run all pending migrations | | `dx migrate-create ` | Create a migration via `artisan make:migration` | | `dx migrate-rollback [--step=N]` | Revert migrations | | `dx migrate-status` | Show migration status | | `dx migrate-fresh` | Drop all tables and re-run all migrations | --- ### Apache | Command | Description | |---|---| | `dx apache validate` | Validate vhost configuration | | `dx apache reload` | Graceful reload (picks up config changes without downtime) | | `dx apache restart` | Full restart | | `dx apache vhosts` | List all loaded virtual hosts | | `dx apache modules` | List all loaded Apache modules | | `dx apache version` | Show Apache version | | `dx apache logs [error\|access]` | Tail error or access logs | --- ### Proxy (Traefik) | Command | Description | |---|---| | `dx proxy start` | Start the Traefik reverse proxy | | `dx proxy stop` | Stop the proxy | | `dx proxy restart` | Restart the proxy | | `dx proxy status` | Show proxy status | | `dx proxy logs` | Tail proxy logs | --- ### Network & hosts | Command | Description | |---|---| | `dx network status` | Show resolved port assignments | | `dx network resolve` | Re-run port resolution and show changes | | `dx network hosts` | Alias for the `hosts` command | | `dx hosts sync` | Write project domain entries to `/etc/hosts` (requires root/sudo) | | `dx hosts remove` | Remove project entries from `/etc/hosts` | | `dx hosts list` | Show current `/etc/hosts` entries for this project | | `dx hosts preview` | Preview entries that would be written | --- ### Workspace | Command | Description | |---|---| | `dx workspace` / `dx ws` | Drop into a subshell where all `dx` commands and framework driver commands are available without the `dx` prefix | --- ## Global flags These flags are accepted by all commands: | Flag | Description | |---|---| | `--framework=NAME` | Override auto-detected framework | | `--debug` | Enable shell trace output (`set -x`) | --- ## Environments Three built-in environments: | Name | Use | |---|---| | `dev` | Development (default) | `qly` | Quality | | `prd` | Production | Environment variables are layered in order: 1. `dxkit/env/globals.env` — shared defaults, source-controlled 2. `.project/artifacts/env/${ENVIRONMENT}.env` — local overrides, gitignored 3. `.project/artifacts/env/${ENVIRONMENT}.resolved.env` — generated resolved values, gitignored Resolved port assignments are written to `.project/artifacts/ports` and loaded back on subsequent runs. Port values in env files may use the `auto:PORT` syntax — dxkit will find the next available port starting from that number. --- ## Framework support Framework detection reads `composer.json` at project root. | Framework | Detected by | Console binary | `dx console` alias | |---|---|---|---| | Yii2 Advanced | `yiisoft/yii2` + advanced template structure | `yii` | `dx yii` | | Yii2 Basic | `yiisoft/yii2` + basic template structure | `yii` | `dx yii` | | Laravel | `laravel/framework` | `artisan` | `dx artisan` | Use `--framework=NAME` to override detection (e.g. `--framework=yii2-advanced`). --- ## Architecture For contributors and maintainers. ``` dx # project-level entrypoint; defines aliases, calls dx::dispatch() dxkit/ ├── bootstrap.sh # loads core.sh, exports PROJECT_ROOT, defines dx::load_modules() ├── core.sh # sources all core subsystems in order ├── core/ # low-level infrastructure │ ├── platform.sh # OS detection, privilege elevation │ ├── context.sh # execution context management │ ├── utils.sh # general utilities │ ├── string.sh # string helpers │ ├── hook.sh # pre/post hook system │ ├── module.sh # module loader │ ├── command.sh # command registration framework │ ├── loader.sh # dynamic file loader │ ├── flag.sh # flag/argument parsing │ └── runtime/runtime.sh # runtime abstraction entry ├── commands/ # one file per command, loaded dynamically by the dispatcher │ ├── build.command.sh │ ├── init.command.sh │ ├── setup.command.sh │ ├── console.command.sh │ ├── apache.command.sh │ ├── hosts.command.sh │ ├── network.command.sh │ ├── proxy.command.sh │ ├── workspace.command.sh │ ├── docker/ # low-level docker subcommands │ └── runtime/ # runtime-abstracted subcommands (used by default) ├── modules/ # reusable function libraries sourced at startup │ ├── app.module.sh # framework abstraction layer (scaffold, init, config, console) │ ├── docker.module.sh # Docker and Docker Compose wrappers │ ├── env.module.sh # environment variable loading and derivation │ ├── log.module.sh # structured logging with icons and context prefixes │ ├── fs.module.sh # cross-platform file write and sed utilities │ ├── network.module.sh # port resolution and persistence │ ├── apache.module.sh # Apache management functions │ ├── proxy.module.sh # proxy abstraction (delegates to docker/proxy.module.sh) │ ├── artifact.module.sh # runtime artifact directory management │ ├── composer.module.sh # composer operations (install, update, require, auth) │ ├── template.module.sh # envsubst-based template rendering │ ├── yii.module.sh # Yii2 shared utilities (exec, env name mapping) │ ├── phpstorm.module.sh # PhpStorm datasource config generation │ ├── docker/ │ │ ├── db.module.sh # database port/path/env helpers (MySQL + MSSQL) │ │ └── proxy.module.sh # Traefik container lifecycle and label generation │ └── framework/ │ ├── yii2-advanced.module.sh │ ├── yii2-basic.module.sh │ └── laravel.module.sh ├── drivers/ # framework-specific command registration │ ├── dispatcher.sh # resolves framework → driver, populates DRIVER_COMMANDS │ ├── yii2/ │ │ ├── driver.sh # registers yii, migrate-* commands │ │ └── migrate.sh # migration file generation helpers │ └── laravel/ │ ├── driver.sh # registers artisan, migrate-* commands │ └── migrate.sh # artisan make:migration wrappers ├── runtime/ # runtime abstraction (Docker vs. native execution) │ ├── runtime.sh # selects active runtime │ ├── docker.runtime.sh # implements runtime::exec via docker compose exec │ └── native.runtime.sh # implements runtime::exec directly on host ├── templates/ # envsubst-rendered config templates │ ├── docker/ │ │ ├── Dockerfile.template │ │ └── docker-compose.yml.template │ ├── apache/ │ │ ├── yii2-advanced/vhosts.conf.template │ │ └── yii2-basic/vhosts.conf.template │ ├── yii2-advanced/ │ │ ├── main-local.php.template │ │ └── components/ # db.mysql, db.mssql, log php templates │ └── yii2-basic/ │ ├── db.mysql.php.template │ └── db.mssql.php.template ├── env/ # layered environment variable files │ ├── globals.env │ ├── dev.env │ ├── qly.env │ └── prd.env └── docker/ └── entrypoint.sh # container entrypoint script ``` ### How command dispatch works 1. `dx [args]` calls `dx::dispatch()` 2. Aliases in `dx` are resolved first (e.g. `sh` → `runtime/shell`, `ls` → `runtime/list`) 3. Built-ins (`help`, `elevate`) are handled inline 4. Driver commands registered in `DRIVER_COMMANDS` (via `dxkit/drivers/dispatcher.sh`) are checked next 5. Remaining commands are matched to files in `dxkit/commands/` and loaded dynamically ### Adding a new command 1. Create `dxkit/commands/.command.sh` with a function `command::()` 2. Optionally register an alias in `dx` under the alias map 3. For framework-specific commands, register the command name in the driver file (`dxkit/drivers//driver.sh`)