CI中composer update必须加--dry-run-v,因其完整执行SAT依赖求解但跳过写入,能提前暴露冲突、无解结论及具体互斥包,避免超时或OOM;不加-v则无法看到关键变更与失败根因。

CI/CD 中 composer install 卡住或失败,90% 不是网络问题,而是依赖解析阶段没做预分析、没设快速失败阈值——直接跑 install 等于把 SAT 求解器丢进黑盒,等它爆内存或超时才报错。
为什么 CI 里 composer update 必须加 --dry-run
CI 环境不能容忍“试错式更新”:composer update 默认触发完整 SAT 求解,一旦约束爆炸(比如多个 "*" 或 "^1.0 || ^2.0 || ^3.0"),可能卡住 5 分钟以上或 OOM。而 composer update --dry-run -v 会提前运行求解器、输出所有尝试的版本组合和冲突路径,不写入 composer.lock,也不下载包。
- 加
-v能看到具体哪两个包在互相conflict,比如Package foo/bar v2.0.0 conflicts with bar/baz v3.1.0 - 若输出末尾出现
Conclusion: Your requirements could not be resolved,说明数学上无解,立刻失败比等 timeout 更早暴露问题 - CI 脚本中应优先执行该命令,失败即
exit 1,避免浪费后续构建资源
怎么让 composer install 在解析失败时秒退
composer install 本身不支持“提前终止 SAT 求解”,但它读 composer.lock 时会校验一致性:如果 lock 文件里某个包的 dist.url 或 source.reference 已失效(如私有 repo 权限变更),它会在下载前就报错,而非卡在解析阶段。
- 确保
composer.lock提交到 Git —— 这是快速失败的前提;没 lock 就等于强制update,失去可控性 - CI 中先运行
composer validate,检查composer.json语法、平台约束(如php >=8.1)是否与当前 PHP 版本匹配,不匹配直接退出 - 用
composer install --no-dev --no-scripts --no-plugins剥离非必要环节,减少干扰变量;尤其禁用--no-scripts可跳过 post-install-cmd 等可能阻塞的钩子
CI 中如何安全重试 HTTP 下载但不掩盖真实错误
HTTP 下载失败常被误判为网络问题,其实多数源于单次请求耐受力不足:--retries=5 无效,是因为默认 http.timeout=60 在 CI 容器里根本撑不过 TLS 握手延迟。
- 永久设置全局 timeout:
composer config -g http.timeout 300(注意不是http-timeout) - CI 脚本中加环境变量
CURL_IPRESOLVE=4强制 IPv4,避开某些云厂商的 IPv6 DNS 不稳定 - 重试前必须清理残留:
rm -rf vendor/ composer.lock,否则部分写入的 zip 文件会导致filemtime(): stat failed这类伪网络错误 -
--no-cache不清理已开始但失败的临时文件,不能替代手动清目录
依赖预分析必须结合项目结构验证
再准的 composer-unused 或 composer prohibits 结果,都建立在项目结构正确的基础上:autoload 映射错位、composer.json 不在根目录、vendor/ 被嵌套,会让所有分析失真。
- CI 启动时先跑
composer diagnose,它会检查composer.json位置、vendor/是否可写、CA 证书路径是否有效 - 用
composer show --locked --format=json | jq -S .导出 lock 内容,diff 时只比对关键字段(name、version、dist.shasum),忽略时间戳和生成工具版本 - 别信
composer show --tree的视觉层级——它受终端宽度影响,CI 中要用composer show --tree --no-ansi | sort才能稳定 diff
真正难的不是让 Composer 快起来,而是让它在出问题前就告诉你“这里一定不行”。SAT 求解器不会说话,但 --dry-run、validate、diagnose 这三个命令就是它的翻译器——漏掉任何一个,你就得靠日志猜。


















