Files
ulthon_admin/.sisyphus/plans/docker-dev-mode.md
augushong efc335e78f
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m13s
feat(stack): 新增 docker-dev 开发模式并自动清理文件
- 新增 docker-dev 部署模式,提供包含 nginx+php-fpm、MySQL、Redis、phpMyAdmin 和 Xdebug 的完整 Docker 开发环境
- 在 StackModeService 中重写 applyMode 方法,切换模式时自动删除目标模式中不存在的已管理文件
- 新增 .docker-dev.env 配置文件并纳入 managed_files 管理,切换模式时自动复制或删除
2026-04-30 22:38:22 +08:00

569 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 新增 docker-dev 部署模式
## TL;DR
> **Quick Summary**: 在 `source/stack/` 下新增 `docker-dev` 模式,提供完整的 Docker 开发环境nginx+php-fpm 并发处理、内置定时器、MySQL、Redis、phpMyAdmin、Xdebug 调试),替代 `php think run` 的单线程限制。
>
> **Deliverables**:
> - `source/stack/docker-dev/Dockerfile` — 基于 full 模式 + Xdebug + 开发优化 PHP 配置
> - `source/stack/docker-dev/docker-compose.yaml` — 多服务编排app、mysql、redis、phpmyadmin
> - `source/stack/docker-dev/.docker-dev.env` — 开发环境专用配置HOSTNAME=mysql 等)
> - `source/stack/stack.json` — 注册 docker-dev 模式 + managed_files 增加 .docker-dev.env
> - `app/common/service/stack/StackModeService.php` — 重写 applyMode切换时自动删除多余文件
> - `.gitignore` — 排除 `docker-dev/` 数据目录
>
> **Estimated Effort**: Medium
> **Parallel Execution**: YES - 3 waves
> **Critical Path**: Task 1-5 (parallel) → Task 6 → Task 7 → F1-F4
---
## Context
### Original Request
开发环境中 `php think run` 是单线程的,无法并发处理请求,也缺少定时器等完整运行支持。需要增加一个 Docker 开发模式,提供生产级运行环境用于本地开发调试。
### Interview Summary
**Key Discussions**:
- 镜像基础:基于 full 模式FROM php:8.2-fpm-bookworm不依赖预构建基础镜像
- 服务组件MySQL 8 + Redis + phpMyAdmin + Xdebug
- 环境配置:独立文件 `.docker-dev.env`,不替换 `.example.env`;纳入 managed_files 管理
- 端口映射:`1+原端口` 方案8800/13306/16379/18888避免与本地服务冲突
- 数据持久化MySQL/Redis 数据映射到项目根目录 `./docker-dev/`
- 多余文件清理:切换回 default 时,自动删除目标模式不存在的已管理文件(需重写 applyMode
- 无需 CI/CDdev 模式不提供 `.gitea/workflows/` 文件,自动 fallback 到 default
**Research Findings**:
- Metis 确认ThinkPHP `Env::load()``parse_ini_file()` 的结果直接覆盖 `$_ENV``.env` 文件值始终优先于 Docker 环境变量
- `docker/run.sh` 已内置 timer + nginx + php-fpm 启动逻辑,无需修改
- `conf.d/` 按 ASCII 排序加载dev 配置文件需命名为 `zzz-` 前缀以覆盖 `zz-phprun.ini`
- 当前 `applyMode` 只做复制,不做删除;`rollback` 有删除逻辑可参考
### Metis Review
**Identified Gaps** (addressed):
- 环境变量优先级反转:不依赖 docker-compose environment 覆盖,改用独立 `.docker-dev.env` 文件
- conf.d 文件排序dev PHP 配置命名为 `zzz-dev.ini` 确保最后加载
- Linux 兼容性:添加 `extra_hosts: host.docker.internal:host-gateway`
- MySQL 启动依赖:使用 `healthcheck` + `depends_on: condition: service_healthy`
- Xdebug 与 JIT 冲突dev 配置中彻底关闭 opcache
---
## Work Objectives
### Core Objective
创建一个开箱即用的 Docker 开发环境模式,解决 `php think run` 的并发限制和定时器缺失问题同时集成常用开发服务MySQL、Redis、phpMyAdmin、Xdebug
### Concrete Deliverables
- `source/stack/docker-dev/Dockerfile`
- `source/stack/docker-dev/docker-compose.yaml`
- `source/stack/docker-dev/.docker-dev.env`
- `source/stack/stack.json` (更新 modes + managed_files)
- `app/common/service/stack/StackModeService.php` (重写 applyMode 增加自动删除)
- `.gitignore` (添加 `/docker-dev/`)
### Definition of Done
- [ ] `php think admin:stack:mode list` 输出包含 docker-dev
- [ ] `php think admin:stack:mode use docker-dev` 成功切换,根目录出现 `.docker-dev.env`
- [ ] `copy .docker-dev.env .env``docker compose build` 构建成功
- [ ] `docker compose up -d` 所有 4 个服务运行正常
- [ ] 应用可正常连接 MySQL端口 13306
- [ ] Xdebug 扩展已加载
- [ ] opcache 已关闭、display_errors 已开启
- [ ] `php think admin:stack:mode use default` 切回后,`.docker-dev.env` 从根目录自动删除
### Must Have
- Dockerfile 基于 `php:8.2-fpm-bookworm`,包含 full 模式的所有 PHP 扩展 + Xdebug
- docker-compose 包含 app、mysql、redis、phpmyadmin 四个服务
- 端口使用 `1+原端口` 方案HTTP 8800、MySQL 13306、Redis 16379、phpMyAdmin 18888
- MySQL 带健康检查app 服务等待 MySQL 就绪后再启动
- 开发优化的 PHP 配置(关闭 opcache、开启 display_errors
- Xdebug 配置client_host=host.docker.internaltrigger 模式)
- Linux 兼容性extra_hosts
- 数据持久化到 `./docker-dev/`
- `.docker-dev.env` 纳入 managed_files`.example.env` 不动
- 切换模式时自动删除目标模式不存在的已管理文件
### Must NOT Have (Guardrails)
- 不修改 `docker/run.sh`(复用现有启动逻辑)
- 不修改 `extend/base/` 下的任何文件(在 app 层重写 applyMode
- 不修改 `.example.env`
- 不修改已有模式default、full、base-build的文件内容
- 不创建 `source/stack/docker-dev/docker/` 子目录
- 不创建 `.gitea/workflows/` 文件
- 不在 docker-compose `environment:` 中设置 ThinkPHP 数据库配置(无效)
- 不使用 `composer install --no-dev`
- 不设置 `opcache.jit` 相关配置(与 Xdebug 冲突)
---
## Verification Strategy
> **ZERO HUMAN INTERVENTION** - ALL verification is agent-executed.
### Test Decision
- **Automated tests**: None基础设施配置 + 小范围代码修改)
- **Framework**: N/A
### QA Policy
Every task includes agent-executed QA scenarios.
Evidence saved to `.sisyphus/evidence/task-{N}-{scenario-slug}.{ext}`.
---
## Execution Strategy
### Parallel Execution Waves
```
Wave 1 (Start Immediately - 所有文件创建可并行):
+-- Task 1: 创建 source/stack/docker-dev/Dockerfile [unspecified-high]
+-- Task 2: 创建 source/stack/docker-dev/docker-compose.yaml [unspecified-high]
+-- Task 3: 创建 source/stack/docker-dev/.docker-dev.env [quick]
+-- Task 4: 更新 source/stack/stack.json [quick]
+-- Task 5: 更新 .gitignore [quick]
Wave 2 (After Task 4 - 代码修改):
+-- Task 6: 重写 app 层 StackModeService (depends: 4) [deep]
Wave 3 (After ALL - 集成验证):
+-- Task 7: 模式切换与 Docker 构建集成验证 (depends: 1-6) [deep]
Wave FINAL:
+-- F1-F4: 并行审查
Critical Path: Task 4 → Task 6 → Task 7 → F1-F4
Max Concurrent: 5 (Wave 1)
```
### Dependency Matrix
| Task | Depends On | Blocks | Wave |
|------|-----------|--------|------|
| 1 | - | 7 | 1 |
| 2 | - | 7 | 1 |
| 3 | - | 7 | 1 |
| 4 | - | 6, 7 | 1 |
| 5 | - | 7 | 1 |
| 6 | 4 | 7 | 2 |
| 7 | 1-6 | F1-F4 | 3 |
### Agent Dispatch Summary
- **Wave 1**: 5 agents - T1 `unspecified-high`, T2 `unspecified-high`, T3-T5 `quick`
- **Wave 2**: 1 agent - T6 `deep`
- **Wave 3**: 1 agent - T7 `deep`
- **FINAL**: 4 agents - F1 `oracle`, F2-F3 `unspecified-high`, F4 `deep`
---
## TODOs
- [x] 1. 创建 `source/stack/docker-dev/Dockerfile`
**What to do**:
- 基于 `source/stack/full/Dockerfile` 创建,保留全部内容
- PHP 扩展安装末尾追加 `install-php-extensions xdebug`
- `composer install` 去掉 `--no-dev``--no-autoloader`
- `composer dump-autoload` 去掉所有优化参数
-`RUN` 写入 `/usr/local/etc/php/conf.d/zzz-dev.ini`
```ini
opcache.enable = 0
opcache.jit_buffer_size = 0
display_errors = On
error_reporting = E_ALL
```
- 用 `RUN` 写入 `/usr/local/etc/php/conf.d/zzz-xdebug.ini`
```ini
xdebug.mode = debug,develop
xdebug.start_with_request = trigger
xdebug.client_host = host.docker.internal
xdebug.client_port = 9003
xdebug.discover_client_host = true
xdebug.idekey = DOCKER_DEV
```
**Must NOT do**: 不创建 docker/ 子目录、不修改 run.sh、不用 --no-dev
**Recommended Agent Profile**: `unspecified-high`, Skills: `[]`
**Parallelization**: Wave 1, Blocks: 7, Blocked By: None
**References**:
- `source/stack/full/Dockerfile` — 基础模板
- `docker/zz-phprun.ini` — 需要覆盖的配置项
- `docker/run.sh` — 第 8-9 行复制 zz-phprun.ini 到 conf.d
**QA Scenarios**:
```
Scenario: 包含 Xdebug 和开发配置
Tool: Bash
Steps:
1. findstr "xdebug" source/stack/docker-dev/Dockerfile → 含 install-php-extensions xdebug
2. findstr "zzz-dev.ini" source/stack/docker-dev/Dockerfile → 存在
3. findstr "zzz-xdebug.ini" source/stack/docker-dev/Dockerfile → 存在
4. findstr "no-dev" source/stack/docker-dev/Dockerfile → 无输出
Evidence: .sisyphus/evidence/task-1-check.txt
```
**Commit**: YES - `feat(stack): 新增 docker-dev 开发模式`
---
- [x] 2. 创建 `source/stack/docker-dev/docker-compose.yaml`
**What to do**:
- 4 个服务:
**app** (`ulthon_admin`):
- `build` from Dockerfile, `restart: unless-stopped`
- `ports: "8800:80"`, `volumes: ./:/var/www/html`
- `extra_hosts: host.docker.internal:host-gateway`
- `depends_on: mysql: condition: service_healthy`
**mysql**:
- `image: mysql:8.0`, `ports: "13306:3306"`
- `environment: MYSQL_ROOT_PASSWORD=root, MYSQL_DATABASE=ulthon`
- `volumes: ./docker-dev/mysql:/var/lib/mysql`
- `healthcheck: mysqladmin ping, interval 10s, retries 5`
**redis**:
- `image: redis:7-alpine`, `ports: "16379:6379"`
- `volumes: ./docker-dev/redis:/data`
**phpmyadmin**:
- `image: phpmyadmin:latest`, `ports: "18888:80"`
- `environment: PMA_HOST: mysql`
- `depends_on: mysql: condition: service_healthy`
- `name: ulthon_admin`,格式与 default/docker-compose.yaml 一致
**Must NOT do**: 不在 app environment 设 DATABASE_*、不用 restart: always
**Recommended Agent Profile**: `unspecified-high`, Skills: `[]`
**Parallelization**: Wave 1, Blocks: 7, Blocked By: None
**References**:
- `source/stack/default/docker-compose.yaml` — 格式参考
**QA Scenarios**:
```
Scenario: 语法和内容验证
Tool: Bash
Steps:
1. docker compose -f source/stack/docker-dev/docker-compose.yaml config → 无错误
2. findstr "8800" source/stack/docker-dev/docker-compose.yaml → 存在
3. findstr "13306" source/stack/docker-dev/docker-compose.yaml → 存在
4. findstr "16379" source/stack/docker-dev/docker-compose.yaml → 存在
5. findstr "18888" source/stack/docker-dev/docker-compose.yaml → 存在
6. findstr "mysqladmin" source/stack/docker-dev/docker-compose.yaml → 存在
7. findstr "host.docker.internal" source/stack/docker-dev/docker-compose.yaml → 存在
Evidence: .sisyphus/evidence/task-2-check.txt
```
**Commit**: YES - `feat(stack): 新增 docker-dev 开发模式`
---
- [x] 3. 创建 `source/stack/docker-dev/.docker-dev.env`
**What to do**:
- 基于根目录 `.example.env` 创建
- 顶部注释:`# Docker 开发模式配置 - 使用方式: copy .docker-dev.env .env`
- 修改:`HOSTNAME=mysql`, `DATABASE=ulthon`, `USERNAME=root`, `PASSWORD=root`, `HOSTPORT=13306`
- 其余不变
**Must NOT do**: 不修改 `.example.env`
**Recommended Agent Profile**: `quick`, Skills: `[]`
**Parallelization**: Wave 1, Blocks: 7, Blocked By: None
**References**: `.example.env`
**QA Scenarios**:
```
Scenario: 配置项验证
Tool: Bash
Steps:
1. findstr "HOSTNAME" source/stack/docker-dev/.docker-dev.env → HOSTNAME=mysql
2. findstr "HOSTPORT" source/stack/docker-dev/.docker-dev.env → HOSTPORT=13306
3. findstr "USERNAME" source/stack/docker-dev/.docker-dev.env → USERNAME=root
4. findstr "PASSWORD" source/stack/docker-dev/.docker-dev.env → PASSWORD=root
5. findstr "DATABASE" source/stack/docker-dev/.docker-dev.env → DATABASE=ulthon
Evidence: .sisyphus/evidence/task-3-check.txt
```
**Commit**: YES - `feat(stack): 新增 docker-dev 开发模式`
---
- [x] 4. 更新 `source/stack/stack.json`
**What to do**:
- `managed_files` 末尾追加 `".docker-dev.env"`
- `modes` 添加:
```json
"docker-dev": {
"description": "Docker 开发模式nginx+php-fpm+MySQL+Redis+phpMyAdmin+Xdebug",
"author_only": false
}
```
**Must NOT do**: 不改 default_mode、不改已有模式
**Recommended Agent Profile**: `quick`, Skills: `[]`
**Parallelization**: Wave 1, Blocks: 6 & 7, Blocked By: None
**References**: `source/stack/stack.json`
**QA Scenarios**:
```
Scenario: JSON 和注册验证
Tool: Bash
Steps:
1. php -r "json_decode(file_get_contents('source/stack/stack.json')); echo json_last_error()===JSON_ERROR_NONE?'VALID':'INVALID';" → VALID
2. php -r "$j=json_decode(file_get_contents('source/stack/stack.json'),true); echo count($j['managed_files']);" → 4
3. php think admin:stack:mode list → 包含 docker-dev
Evidence: .sisyphus/evidence/task-4-check.txt
```
**Commit**: YES - `feat(stack): 新增 docker-dev 开发模式`
---
- [x] 5. 更新 `.gitignore`
**What to do**: 添加 `/docker-dev/`
**Recommended Agent Profile**: `quick`, Skills: `[]`
**Parallelization**: Wave 1, Blocks: 7, Blocked By: None
**References**: `.gitignore`
**QA Scenarios**:
```
Scenario: 排除规则
Tool: Bash
Steps:
1. findstr "docker-dev" .gitignore → 输出 /docker-dev/
Evidence: .sisyphus/evidence/task-5-check.txt
```
**Commit**: YES - `feat(stack): 新增 docker-dev 开发模式`
---
- [x] 6. 重写 `app/common/service/stack/StackModeService.php`,增加自动删除
**What to do**:
- 在 `app/common/service/stack/StackModeService.php` 中重写 `applyMode` 方法
- 调用 `parent::applyMode()` 执行原有的复制逻辑
- 然后遍历 `managed_files`,对于**目标模式和 default 都不存在**的已管理文件,从根目录删除
- 输出日志提示用户哪些文件被清理
**实现逻辑**(伪代码):
```php
public function applyMode(string $mode, string $operator = ''): array
{
$result = parent::applyMode($mode, $operator);
$stackConfig = json_decode(file_get_contents(root_path() . 'source/stack/stack.json'), true);
$managedFiles = $stackConfig['managed_files'] ?? [];
foreach ($managedFiles as $file) {
$targetPath = root_path() . $file;
if (!file_exists($targetPath)) {
continue;
}
$modeFile = root_path() . "source/stack/{$mode}/{$file}";
$defaultFile = root_path() . "source/stack/default/{$file}";
if (!file_exists($modeFile) && !file_exists($defaultFile)) {
@unlink($targetPath);
// 记录到 result 或日志
}
}
return $result;
}
```
**Must NOT do**:
- 不修改 `extend/base/common/service/stack/StackModeServiceBase.php`
- 不删除非 managed_files 中的文件
**Recommended Agent Profile**: `deep`, Skills: [`ulthon-base-app-architecture`]
**Parallelization**: Wave 2, Blocks: 7, Blocked By: Task 4
**References**:
- `extend/base/common/service/stack/StackModeServiceBase.php` — applyMode 完整实现(第 106-178 行)
- `app/common/service/stack/StackModeService.php` — 当前空类,在这里重写
- `source/stack/stack.json` — managed_files 列表
**WHY Each Reference Matters**:
- `StackModeServiceBase.php`: 必须理解 parent::applyMode() 的备份和复制逻辑,确保重写不丢失任何步骤
- `StackModeService.php`: 要修改的文件
- `stack.json`: managed_files 是清理逻辑的判断依据
**QA Scenarios**:
```
Scenario: 切换到 docker-dev 后 .docker-dev.env 出现
Tool: Bash
Steps:
1. php think admin:stack:mode use docker-dev -f
2. dir .docker-dev.env → 存在
Evidence: .sisyphus/evidence/task-6-appear.txt
Scenario: 切回 default 后 .docker-dev.env 被自动删除
Tool: Bash
Steps:
1. php think admin:stack:mode use default -f
2. dir .docker-dev.env 2>nul → 不存在
Evidence: .sisyphus/evidence/task-6-disappear.txt
Scenario: rollback 恢复
Tool: Bash
Steps:
1. php think admin:stack:mode use docker-dev -f
2. php think admin:stack:mode use default -f删除 .docker-dev.env
3. php think admin:stack:mode rollback <backup_id>
4. dir .docker-dev.env → 恢复存在
Evidence: .sisyphus/evidence/task-6-rollback.txt
```
**Commit**: YES - `feat(stack): 切换模式时自动删除多余的管理文件`
- Files: `app/common/service/stack/StackModeService.php`
---
- [x] 7. 模式切换与 Docker 构建集成验证
**What to do**:
- 端到端验证所有组件
- 验证模式切换流程(含自动删除)
- 验证 Docker 构建和启动
- 验证 PHP 配置、Xdebug、服务连通性
- 验证端口8800/13306/16379/18888
- 发现问题则修复并重验证
**Recommended Agent Profile**: `deep`, Skills: [`ulthon-cli-reference`]
**Parallelization**: Wave 3, Blocks: F1-F4, Blocked By: Tasks 1-6
**References**:
- `source/stack/README.md` — 模式切换 fallback 规则
- `docker/run.sh` — 容器启动逻辑
**QA Scenarios**:
```
Scenario: 完整切换流程
Tool: Bash
Steps:
1. copy Dockerfile Dockerfile.bak && copy docker-compose.yaml docker-compose.yaml.bak
2. php think admin:stack:mode use docker-dev -f
3. fc Dockerfile source\stack\docker-dev\Dockerfile → 一致
4. fc docker-compose.yaml source\stack\docker-dev\docker-compose.yaml → 一致
5. dir .docker-dev.env → 存在
Evidence: .sisyphus/evidence/task-7-switch.txt
Scenario: Docker 构建和启动
Tool: Bash
Steps:
1. copy .docker-dev.env .env
2. docker compose build --no-cache → 成功
3. docker compose up -d
4. timeout /t 30 /nobreak >nul
5. docker compose ps → 4 个 running/healthy
Evidence: .sisyphus/evidence/task-7-build.txt
Scenario: PHP 配置和 Xdebug
Tool: Bash
Steps:
1. docker compose exec ulthon_admin php -r "echo ini_get('opcache.enable');" → 0
2. docker compose exec ulthon_admin php -r "echo ini_get('display_errors');" → 1
3. docker compose exec ulthon_admin php -m | findstr xdebug → xdebug
Evidence: .sisyphus/evidence/task-7-php.txt
Scenario: 连通性和端口
Tool: Bash
Steps:
1. docker compose exec ulthon_admin php -r "$c=@fsockopen('mysql',3306); echo $c?'OK':'FAIL';" → OK
2. docker compose exec ulthon_admin php -r "$c=@fsockopen('redis',6379); echo $c?'OK':'FAIL';" → OK
3. curl -s -o nul -w "%%{http_code}" http://localhost:8800/admin → 200/302
4. curl -s -o nul -w "%%{http_code}" http://localhost:18888/ → 200
Evidence: .sisyphus/evidence/task-7-connect.txt
Scenario: 数据目录
Tool: Bash
Steps:
1. dir docker-dev\mysql → 存在
2. dir docker-dev\redis → 存在
Evidence: .sisyphus/evidence/task-7-data.txt
Scenario: 切回 default + 清理
Tool: Bash
Steps:
1. docker compose down
2. php think admin:stack:mode use default -f
3. dir .docker-dev.env 2>nul → 不存在
4. copy Dockerfile.bak Dockerfile && copy docker-compose.yaml.bak docker-compose.yaml
5. del *.bak .env
6. git status → 无残留
Evidence: .sisyphus/evidence/task-7-cleanup.txt
```
**Commit**: NO (验证任务)
---
## Final Verification Wave (MANDATORY)
- [x] F1. **Plan Compliance Audit** — `oracle` — APPROVE
Must Have [10/10] | Must NOT Have [9/9]
- [x] F2. **Code Quality Review** — `unspecified-high` — APPROVE (after P0/P1 fixes)
HOSTPORT 3306 fix applied, backup_id protection added
- [x] F3. **Real Manual QA** — `unspecified-high` — APPROVE (atlas manual verification)
Scenarios [6/6 pass]: mode list, switch docker-dev, file verification, switch default auto-delete, rollback restore, cleanup
- [x] F4. **Scope Fidelity Check** — `deep` — APPROVE (after git checkout restore)
Tasks [6/6] | Contamination [CLEAN]
---
## Commit Strategy
- **Commit 1**: `feat(stack): 新增 docker-dev 开发模式`
- Files: `source/stack/docker-dev/Dockerfile`, `source/stack/docker-dev/docker-compose.yaml`, `source/stack/docker-dev/.docker-dev.env`, `source/stack/stack.json`, `.gitignore`
- **Commit 2**: `feat(stack): 切换模式时自动删除多余的管理文件`
- Files: `app/common/service/stack/StackModeService.php`
---
## Success Criteria
### Verification Commands
```bash
php think admin:stack:mode list # 包含 docker-dev
php think admin:stack:mode use docker-dev -f # 切换成功
dir .docker-dev.env # 文件存在
copy .docker-dev.env .env # 配置就绪
docker compose up -d # 4 服务运行
curl http://localhost:8800/admin # 可访问
php think admin:stack:mode use default -f # 切回
dir .docker-dev.env 2>nul # 文件已自动删除
```
### Final Checklist
- [ ] All "Must Have" present
- [ ] All "Must NOT Have" absent
- [ ] `docker compose config` 通过
- [ ] 未修改 `docker/run.sh`
- [ ] 未修改 `extend/base/`
- [ ] `.example.env` 未被修改
- [ ] 切回 default 时 `.docker-dev.env` 自动删除