CI无法自动感知composer.json中dev-main等分支的远程变更,仅依赖composer.lock哈希;上游推送新提交后,lock中commit hash过期却静默复用旧包,必须显式执行composer update --with-dependencies更新lock并提交。

CI/CD 里怎么发现 composer.json 中的 dev-main 或 #dev 分支变动?
CI 不会自动感知 composer.json 里 "package/name": "dev-main" 这类开发分支的远程变更——它只认 composer.lock 的哈希。一旦上游 dev-main 推了新提交,composer.lock 里记录的 commit hash 就过期了,但 CI 不报错,只会静默复用旧包。
真正能触发重装的只有两件事:composer.lock 文件被修改并提交,或显式执行 composer update package/name。所以热修开发分支,必须强制更新 lock 文件:
- 本地改完
composer.json后,立刻运行composer update --with-dependencies --no-interaction,再提交生成的新composer.lock - CI 脚本中禁用
composer install的缓存跳过逻辑:加--ignore-platform-reqs(仅调试期)或在必要时用composer update package/name --no-install单独刷新 lock 条目 - Git hook(如 pre-commit)里跑
composer validate --strict,可提前捕获composer.json和lock不一致
dev 分支依赖导致 vendor 缓存失效,该怎么应对?
dev-main、dev-feature/x 这类分支依赖,在 Composer 缓存中是以 dist 形式 fallback 到 zip 包的,但 zip URL 里含 commit hash;每次上游 push,hash 变,URL 就变,~/.composer/cache/files 里就多一个新 entry,旧缓存不会被复用——不是缓存没起作用,是根本没命中。
这不是 bug,是设计使然。解决思路不是“让缓存复用”,而是“让 CI 明确知道该清哪部分”:
- CI 缓存 key 必须包含
${{ hashFiles('**/composer.lock') }},不能只靠 OS 或 PHP 版本;否则 lock 变了但 key 不变,缓存就脏了 - 不要缓存
vendor/,尤其含 dev 分支时——不同 commit 的 autoload classmap 冲突概率极高,Cannot declare class错误基本都源于此 - 若必须高频更新 dev 分支,建议在 CI 中加一步:
composer clear-cache --no-interaction前置清理,避免旧 commit zip 残留干扰
热修后 CI 构建失败,常见卡点在哪?
现象常是 composer install 卡在 Lock file operations: 1 install, 0 updates 后无响应,或报 Class not found。这通常不是网络或权限问题,而是 dev 分支引入了未声明的平台依赖或扩展要求:
- 检查目标环境是否装了新代码依赖的扩展,比如新增了
ext-sodium,但 CI runner 镜像没装——php -m | grep sodium要进脚本验证 - 确认
composer.json里的"platform"字段(如"ext-gd": "8.2.0")和实际 PHP 环境匹配;不匹配时 Composer 2.x 会跳过某些包,且不报错 - 私有仓库的 dev 分支若需 auth,
auth.json不能提交,要用composer config http-basic.repo.example.com $USER $TOKEN动态注入,且确保 token 有读取该分支的权限 - 如果用了
--classmap-authoritative,而热修新增了类但没重新生成 autoload,就会直接 fatal;此时 CI 必须保证post-autoload-dump脚本被执行,或手动加--no-autoloader && composer dump-autoload -o -a
为什么热修后线上行为不一致?
最隐蔽的问题是:CI 用了 --no-dev,但热修的 dev 分支代码里混了 require-dev 工具类(比如某测试工具提供了运行时辅助函数),这些类在 CI 构建时被剔除,但本地开发没加 --no-dev,导致线上找不到类。
这不是缓存或镜像问题,是依赖边界失控:
- 所有热修分支的代码,必须通过
composer require --no-update显式声明为require,而非靠require-dev临时带入 - CI 构建命令里
--no-dev是铁律,不能为“方便调试”临时去掉;否则构建产物不可控 - 如果确实需要运行时依赖 dev 工具(极少见),应改用
config.autoload-dev+composer dump-autoload --dev,而不是把它们放进require-dev
dev 分支热修不是“快速上线”,而是“高风险操作”——它的不可预测性主要来自 autoload 生成时机、平台扩展差异和依赖声明模糊,这些点比网络或缓存更难排查。


















