post-update-cmd是Composer在composer update导致composer.lock重写后触发的显式配置钩子,仅响应依赖版本实际变更,不因vendor存在与否或install操作误触发,且受--no-scripts和COMPOSER_COMMAND环境变量控制。

Composer 更新后自动执行脚本不是默认开启的“功能”,而是靠 post-update-cmd 钩子显式配置的响应机制;它不保证每次 composer update 都会运行(比如 lock 未变、--no-scripts 被启用),更不会在 install 时误触发——除非你手抖写错了钩子名。
post-update-cmd 和 post-install-cmd 到底该用哪个?
选 post-update-cmd,前提是你要响应「依赖版本实际变更」这个动作。它只在 composer update 导致 composer.lock 被重写后触发,不因本地 vendor 不存在而误跑。
-
post-install-cmd在composer install时也跑,哪怕只是重装一遍已有依赖(lock 没变),容易重复执行缓存清理、构建等耗时操作 - 如果你的逻辑只和「包升级」相关(比如更新 API 客户端后刷新 SDK 文档),
post-update-cmd是唯一合理入口 - 不要把同一段命令同时塞进两个钩子——CI 流水线里
install --no-dev可能跳过post-update-cmd,但意外触发post-install-cmd,造成行为不一致
怎么防止脚本在不该跑的时候执行?
靠 COMPOSER_COMMAND 环境变量做守门人。Composer 2.2+ 在执行任意脚本前都会注入该变量,值为 install 或 update,PHP 和 shell 都能直接读取。
- PHP 命令要加引号包裹,否则 shell 会提前解析
=:php -r "if(getenv('COMPOSER_COMMAND')==='update'){echo 'ok';}" - Shell 脚本里直接用
if [ "$COMPOSER_COMMAND" = "update" ]; then ... fi - 别混淆
COMPOSER_DEV_MODE——它只表示是否加载了 dev 依赖,和当前是 install 还是 update 无关 - CI 中若用了
composer install --no-dev,post-update-cmd本来就不会触发,无需额外判断
post-update-cmd 执行失败怎么办?
失败默认中断整个 composer update 流程,报错类似 Script handling the post-update-cmd event returned with error code 1。这不是 Composer 的 bug,是你脚本里的命令退出码非 0。
- 用
&&连接多条命令时,前面一条失败会导致后续不执行;改用;或显式检查$?更可控 - 框架命令如
php artisan config:clear在未配置 APP_KEY 或数据库不可达时会挂,建议加|| true忽略非关键错误(仅限开发环境) - 避免在钩子里调用需要交互的命令(如
git commit),CI 环境下会卡住 - 调试时加
echo "DEBUG: running post-update-cmd",确认钩子是否被识别——常见原因是composer.json格式错误或 scripts 缩进不对
为什么有时 post-update-cmd 根本不执行?
最常被忽略的是 --no-scripts 参数。它会彻底屏蔽所有钩子,包括 post-update-cmd,且不会报错提示。
- 生产部署脚本里如果写了
composer install --no-scripts,别指望post-update-cmd能补救 - 某些 Dockerfile 模板或 CI 配置会全局 alias
composer install为带--no-scripts的版本,得去翻构建日志确认 -
composer update在 vendor 已存在、lock 未变、且没加--force时,可能跳过 autoload 重写,进而跳过post-autoload-dump——但post-update-cmd不受此影响,只要 lock 改了就一定触发 - 确保
scripts是顶层字段,没被意外缩进到require或autoload下面
真正难处理的不是“怎么配”,而是判断哪些操作必须放在这里、哪些该拆出去由 CI 显式调用;比如清缓存可以放钩子里,但生成前端资源或推送镜像,最好留在部署流水线里统一控制权限和上下文。











