CI中composer install软链接失效是因为clean checkout导致vendor全新生成,而软链接依赖的宿主机缓存或共享目录在CI runner中不存在且未挂载;需显式配置缓存卷、统一store-dir路径并确保权限可写。

CI里用composer install时软链接为什么失效
因为CI环境默认是clean checkout,vendor目录每次都是全新生成的,而Composer软链接(比如通过composer config --global store-dir或插件生成的符号链接)依赖宿主机已有的缓存或共享目录——但CI runner不保留这些状态,也通常没挂载对应volume。现象是:本地能跑的composer install在CI里报Class not found或autoload失败,实际是链接指向了不存在的路径。
GitLab CI / GitHub Actions中启用软链接的硬性前提
必须显式挂载共享缓存卷,并确保所有job复用同一~/.composer/cache和~/.composer/vendor位置:
- GitHub Actions需在
steps中加actions/cache,key设为composer-${{ hashFiles('**/composer.lock') }},path设为~/.composer/cache - GitLab CI需在
cache:块中声明key: $CI_COMMIT_REF_SLUG并指定paths: ["/root/.composer/cache"] - 必须禁用
--no-cache和COMPOSER_CACHE_DIR被覆盖的情况,否则软链接逻辑根本不会触发
使用composer-shared-plugin时CI配置要点
该插件依赖composer.json中extra.shared-packages字段,但在CI中容易因权限或路径错位导致链接失败:
- 务必在job开头运行
composer config -g store-dir /tmp/composer-shared,把共享包统一导向可写临时目录 - 添加
chmod -R 755 /tmp/composer-shared,避免后续install因权限拒绝创建符号链接 - 不要在
before_script里调用composer update——它会重写vendor结构,破坏已有软链接 - 验证是否生效:运行
ls -la vendor/monolog/monolog,输出应含-> /tmp/composer-shared/monolog/monolog
比软链接更稳的替代方案
多数CI场景下,软链接不是刚需,反而增加不可靠性。真正省时且稳定的做法是:
- 用
--prefer-dist+actions/cache或cache:paths缓存~/.composer/cache,让dist包解压复用 - 预构建PHP基础镜像,在Dockerfile中固化
composer config -g repos.packagist composer https://mirrors.aliyun.com/composer/和DNS设置 - 放弃“共享vendor”,改用
composer install --no-dev --optimize-autoloader --classmap-authoritative直接生成高效autoload
软链接在CI中最容易被忽略的一点:它要求整个构建生命周期内store-dir路径绝对稳定、可写、且不被runner清理——而这三点在绝大多数托管CI服务里都无法默认保证。











