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

21 KiB
Raw Blame History

新增 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 .envdocker 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

  • 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
      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
      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 开发模式


  • 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 开发模式


  • 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 开发模式


  • 4. 更新 source/stack/stack.json

    What to do:

    • managed_files 末尾追加 ".docker-dev.env"
    • modes 添加:
      "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 开发模式


  • 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 开发模式


  • 6. 重写 app/common/service/stack/StackModeService.php,增加自动删除

    What to do:

    • app/common/service/stack/StackModeService.php 中重写 applyMode 方法
    • 调用 parent::applyMode() 执行原有的复制逻辑
    • 然后遍历 managed_files,对于目标模式和 default 都不存在的已管理文件,从根目录删除
    • 输出日志提示用户哪些文件被清理

    实现逻辑(伪代码):

    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

  • 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)

  • F1. Plan Compliance Auditoracle — APPROVE Must Have [10/10] | Must NOT Have [9/9]

  • F2. Code Quality Reviewunspecified-high — APPROVE (after P0/P1 fixes) HOSTPORT 3306 fix applied, backup_id protection added

  • F3. Real Manual QAunspecified-high — APPROVE (atlas manual verification) Scenarios [6/6 pass]: mode list, switch docker-dev, file verification, switch default auto-delete, rollback restore, cleanup

  • F4. Scope Fidelity Checkdeep — 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

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 自动删除