dx/README.md

321 lines
12 KiB
Markdown

# 🛠️ 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 <cmd> [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 <cmd> [args]` | Run a framework console command |
| `dx yii <cmd>` | Alias for Yii2 (`php yii <cmd>`) |
| `dx artisan <cmd>` | Alias for Laravel (`php artisan <cmd>`) |
---
### Migration shortcuts
These are resolved by the active framework driver.
**Yii2**
| Command | Description |
|---|---|
| `dx migrate` | Run all pending migrations |
| `dx migrate-create <name>` | 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 <name>` | 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 <command> [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/<name>.command.sh` with a function `command::<name>()`
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/<framework>/driver.sh`)