github actions 中 composer install 总重装是因为每次运行都在干净容器中,vendor/ 和 ~/.composer/cache 均丢失;需用 actions/cache 缓存 vendor/(键含 composer.lock 哈希)和 ~/.composer/cache(键含 php 版本及 lock 哈希),并配置 git 凭据、合理使用 --no-dev。

GitHub Actions 里 composer install 为什么总重装?
因为默认每次运行都是干净容器,vendor/ 和 Composer 的全局缓存(~/.composer/cache)全丢。不手动干预,composer install 就得重新下载、解压、安装所有包,动辄 2–5 分钟。
用 actions/cache 缓存 vendor/ 目录最直接
缓存 vendor/ 是见效最快的方案,前提是锁定依赖版本(即存在 composer.lock)。关键点是缓存键要包含 composer.lock 的哈希值,否则 lock 文件一变,旧缓存就失效或引发冲突。
实操建议:
- 用
hashFiles('**/composer.lock')生成缓存键,比只用composer.lock时间戳更可靠 - 缓存路径写成
./vendor(相对路径),和composer install --no-interaction --prefer-dist配合使用 - 加个
if: github.event_name == 'pull_request' || github.event_name == 'push'条件,避免在 fork 的 workflow 中误触发缓存写入
steps:
- uses: actions/cache@v4
with:
path: ./vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}缓存 ~/.composer/cache 能省下下载时间,但有坑
这个缓存能跳过包归档(.zip/.tar)的重复下载,对首次 composer install 或 composer update 有效。但它不是万能的——Composer 8+ 默认启用 cache-vcs,本地 Git 克隆也会被缓存,而 GitHub Actions 的容器默认没配 Git 凭据,导致缓存命中后仍可能报 Failed to clone https://github.com/xxx。
常见错误现象:
Cloning into '/tmp/composer-cache/vcs/xxx'... fatal: could not read Username- 缓存命中率高,但某次突然变慢,日志显示大量
Downloading xxx.zip
解决办法:
- 加一步
git config --global url."https://oauth2:${{ secrets.PAT }}@github.com/".insteadOf "https://github.com/"(需提前创建 Personal Access Token 并设为 secret) - 缓存键必须包含 PHP 版本:
${{ runner.os }}-php-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }},否则 PHP 8.1 和 8.2 共用同一份缓存会出错
别忽略 composer install --no-dev 在 CI 中的实际影响
CI 环境通常不需要 require-dev,但漏掉 --no-dev 会让缓存体积翻倍、安装时间多出 30%–60%,尤其当项目用了 phpunit、infection 这类重型 dev 依赖时。
注意点:
-
--no-dev不影响autoload-dev的加载逻辑,只是不装 dev 包,不影响自动加载配置本身 - 如果测试脚本依赖某个 dev 包的二进制(如
phpstan),那就不能简单加--no-dev,得拆成两个步骤:先install --no-dev,再单独composer require --dev phpstan/phpstan --no-interaction - 某些私有包若未在
repositories中声明 type=package,--no-dev可能导致其被跳过,检查composer show --platform输出确认
缓存和安装逻辑看着简单,但 Composer 的锁文件解析、PHP 版本感知、Git 凭据链路,三者只要一个没对齐,缓存就变成幻觉——跑得慢还是小事,装错版本才真难排查。










