CI中执行Composer自定义脚本必须显式调用且环境干净可控;脚本名需严格匹配composer.json中scripts字段,统一用composer run 调用;PHP脚本开头须手动require vendor/autoload.php;命令需用相对路径并显式注入环境变量;关键任务保持非0退出中断,检查类工具可用|| true绕过失败。

CI 中执行 Composer 自定义脚本,核心就一条:必须显式调用,且环境要干净可控;别指望它自动跑、也别信本地能过 CI 就稳。
脚本名拼写和调用方式必须严格匹配
CI 环境里没有 IDE 提示,拼错一个字符就静默失败。比如你写的是 "ci:build",但 CI 脚本里敲了 composer run ci_build 或 composer ci:build,结果都是 Command "ci:build" is not defined 或 Script not found。
- 确认脚本名在
composer.json的scripts顶层字段中,且没被注释或缩进错位 - CI 命令统一用
composer run(Composer ≥2.2)或composer run-script "(加双引号,Windows 必须)" - 运行前先加一步验证:
composer run --list | grep ci:,确保脚本真被识别到了
PHP 脚本访问项目类时 autoload 必须手动加载
CI 容器里执行 php build.php 报 Class 'App\Build' not found?不是自动加载配置错了,是脚本根本没 require vendor/autoload.php。
- 不要依赖 Composer “自动帮你加载”,它不会做这件事
- 在 PHP 脚本开头第一行写:
require __DIR__ . '/vendor/autoload.php'; - 避免用
php -r "new App\Build();"这类内联命令——-r不加载 autoload,必报错 - 如果脚本复用框架命令(如 Laravel 的
artisan),优先走php artisan xxx,而不是自己 new 实例
环境变量和路径在 CI 中不可靠,得显式控制
本地 phpunit 能跑,CI 里报 command not found?大概率是 vendor/bin/ 没进 $PATH,或者当前工作目录不是项目根目录。
- 所有命令用相对路径:
"test": "./vendor/bin/phpunit --no-coverage",别写phpunit - 环境变量不能靠
.env文件自动加载——Composer 不读它;需显式注入,如APP_ENV=staging composer run test - CI 中禁用交互式行为:加
--no-interaction参数,否则php artisan migrate在生产环境会卡住等确认 - 避免 shell 特有语法(如
rm -rf var/cache/*),改用跨平台 PHP:"clear-cache": "php -r \"array_map('unlink', glob('var/cache/*'));\""
CI 中脚本失败必须中断流程,但得区分“该挂”和“不该挂”
数据库迁移失败,CI 继续往下跑部署?那等于把半截 schema 推上生产。但 lint 报几个 warning 就让整个 job 失败,又太激进。
- 关键任务(如
migrate、dump-autoload)保持默认 exit code 行为:非 0 就中断,这是安全底线 - 检查类(如
phpstan、phpcs)可加--no-ansi --quiet控制输出,并用|| true绕过失败(仅限非阻塞型检查) - 慎用
--no-scripts:很多 CI 模板默认加它来提速,但会直接跳过所有post-*钩子,导致迁移、缓存清理全失效 - 钩子绑定要精准:想每次
composer update都跑迁移?别放post-install-cmd,得用post-update-cmd—— 否则 CI 里composer require就不触发
最常被忽略的一点:CI 中的 composer install 默认带 --no-scripts,除非你显式去掉。这意味着即使你写了 "post-install-cmd": "php artisan migrate",它也不会自动执行——你得在 CI 配置里明确写成 composer install --no-interaction,而不是依赖模板默认行为。











