不是镜像源本身导致死锁,而是镜像源配置不一致 + composer.lock 未锁定平台约束,让不同环境解析出不同依赖树;CI卡在“Resolving dependencies…”超2分钟,主因是PHP版本或扩展缺失触发求解器无限回溯,需统一PHP镜像、提交composer.lock、禁用--ignore-platform-reqs并验证platform一致性。

GitLab CI里composer install卡住不动,是不是镜像源问题?
不是镜像源本身导致死锁,而是镜像源配置不一致 + composer.lock 未锁定平台约束,让不同环境解析出不同依赖树。CI里卡在“Resolving dependencies…”超过2分钟,大概率是PHP版本或扩展缺失触发了回溯求解器无限尝试——它在找一个根本不存在的版本交集。
常见诱因:
-
composer.json里写了"php": "^8.1",但CI job用的是PHP 8.0,Composer没直接报错,而是反复尝试降级所有包来适配,最终超时 - 镜像源配置分散在
~/.composer/config.json(全局)、COMPOSER_HOME环境变量、甚至gitlab-ci.yml里用composer config临时设置,三者冲突 - 私有包仓库地址写在
repositories里,但没加"type": "vcs",Composer fallback到packagist.org查不到,又不敢放弃,陷入等待
如何让CI里的composer install不依赖本地镜像配置?
强制CI只认项目级配置,彻底隔离宿主环境干扰。关键不是换镜像,而是切断所有外部配置继承。
在.gitlab-ci.yml中这样写:
script: - composer config --global --unset repos.packagist - composer config --unset repos.packagist - composer config repositories.packagist composer https://mirrors.aliyun.com/composer/ - composer install --no-interaction --prefer-dist --no-scripts
说明:
-
config --global --unset清掉CI runner上可能残留的全局镜像设置(比如之前job留下的) -
config --unset清掉当前项目composer.json里可能存在的repositories覆盖 -
config repositories.packagist显式声明唯一镜像源,且必须用composer类型,不是package或vcs - 绝对不用
--ignore-platform-reqs——它掩盖PHP版本/扩展不匹配,把问题拖到运行时报错
为什么CI里composer update --lock会失败,而本地能过?
因为composer.lock里记录了平台配置哈希(platform-check),CI和本地PHP版本或扩展差异会导致哈希不匹配,update --lock拒绝生成新lock。
验证方式:运行composer show --platform对比CI和本地输出,重点关注php、ext-zip、ext-opcache三项。
安全修复步骤:
- 在CI job开头加
composer validate --strict,提前暴露composer.json与composer.lock平台描述不一致 - 若验证失败,立刻跑
composer update --lock --with-all-dependencies,它会重算整个依赖树并更新平台哈希 - 禁止在CI里执行
composer update无参数命令——它会改vendor/,破坏lock契约 - 把CI用的PHP镜像版本写死,例如
image: php:8.1-cli,别用php:latest
私有包+镜像源混合使用时,怎么避免require-dev引发冲突?
私有包往往通过vcs仓库引入,而require-dev里的测试工具(如phpunit/phpunit)会悄悄拉入不兼容的symfony/console,再跟私有包里硬绑的symfony/console:^5.4打架。
实操要点:
- 在
composer.json的repositories里,为私有包显式指定"type": "vcs",并确保URL是Git SSH或HTTPS可读地址 - 把
require-dev里所有非必要包移到config.platform里模拟存在,例如:"platform": {"phpunit/phpunit": "9.6.13"},避免真实安装 - CI里跑
composer install --no-dev,彻底排除require-dev干扰;只有单元测试job才启用--with-all-dependencies单独装dev依赖 - 检查私有包的
composer.json是否用了"dev-main"——这会让CI每次拉最新commit,lock文件里的commit hash失去意义,应改用"^2.1"加Git tag
最易被忽略的一点:GitLab CI的composer.lock必须提交,且不能被.gitignore过滤。如果CI日志里出现Lock file operations: x installs, y updates,说明它正在重生成lock,而不是按契约安装——这已经脱离了“可重现构建”的底线。


















