ci中应通过git diff检查composer.json或composer.lock是否被修改来决定是否执行composer install,需确保git历史深度足够(如fetch-depth: 2),并用命令替换判断输出非空以避免退出码误判。

CI里怎么用git diff找出被修改的PHP依赖文件
直接靠 composer install 自身没法判断路径变更,得靠 Git 提前筛出是否动了 composer.json 或 composer.lock。CI 脚本里最稳妥的方式是检查这两个文件是否在本次提交中被修改过。
- 推荐用
git diff --name-only HEAD^ HEAD | grep -E '^(composer\.json|composer\.lock)$',注意要确保 CI 环境有足够 git 历史(比如fetch-depth: 2) - 如果用 GitHub Actions,
actions/checkout@v4默认只 fetch 当前 commit,必须显式加fetch-depth: 2,否则HEAD^会失败 - GitLab CI 可用
$CI_COMMIT_BEFORE_SHA替代HEAD^,更可靠:git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | grep -E '^(composer\.json|composer\.lock)$'
Shell 判断逻辑写法别漏掉空输出和退出码
很多人用 grep 后直接跟 && composer install,但 grep 没匹配时默认退出码是 1,会导致整个命令链中断或误判——尤其在 set -e 环境下容易静默跳过安装。
- 正确做法是用命令替换捕获输出,再判断非空:
if [ -n "$(git diff --name-only HEAD^ HEAD | grep -E '^(composer\.json|composer\.lock)$')" ]; then composer install; fi - 别用
grep -q单独判断,它不输出内容,但退出码逻辑在管道里容易受上一个命令影响(比如git diff本身失败时) - 如果 CI 使用缓存(如 Composer cache in GitHub Actions),记得
composer install --no-interaction --prefer-dist,避免交互卡住或本地配置干扰
为什么不能只看 changed-files 列表就跳过 install
有些团队试图解析 PR 中所有变更文件,只要没改 PHP 文件就跳过 composer install,这是危险的。依赖变更的影响不局限于代码路径,而在于锁文件是否生效。
-
composer.json里改了require-dev或config.platform,不改业务代码但会影响测试环境行为 -
composer.lock被 rebase 或手动编辑过,即使没 diff 出差异,也可能导致依赖版本实际不一致 - 某些 CI 场景(如矩阵构建)需要干净 vendor,哪怕没改依赖文件,也建议加个开关控制是否强制 install,比如用
if [[ "$FORCE_COMPOSER_INSTALL" == "true" ]]; then ...
GitHub Actions 示例片段要注意 job-level 权限和缓存键
在 steps 里嵌入判断时,别把 composer install 放进 if 条件的 run 字段里拼接长字符串,可读性差还难调试。
- 推荐拆成两个 step:
Check dependency changes输出 env 变量,再用if: env.HAS_COMPOSER_CHANGE == 'true'控制下一步 - 缓存 key 别只用
composer.lockhash,应包含 PHP 版本和平台配置,例如:php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }} - 如果项目用了
platform.config或自定义repositories,这些变化不会反映在 lock 文件里,需额外监控相关配置项
git diff --name-only 漏掉真实变更。









