根本原因是本地执行 composer install/update 时 composer.lock 具有写权限,导致其被意外重写;最直接防护是设为只读(chmod 444),使 composer 写入失败并报错,强制人工干预。

为什么 composer.lock 被意外修改了
根本原因不是 Composer 本身允许乱改,而是你本地执行了 composer install 或 composer update 时,当前目录有写权限,且没加任何防护。尤其在 CI/CD 环境或多人共用开发机时,composer.lock 被悄悄重写,导致部署行为不一致——比如某次 git pull 后直接 composer install,结果它却偷偷触发了依赖更新逻辑(因 composer.json 和 lock 不匹配),最终装了一堆不该装的版本。
用 chmod 给 composer.lock 加只读锁最直接
Linux/macOS 下一行命令即可生效,无需改配置、不依赖插件:
chmod 444 composer.lock
这会让文件对所有用户(owner/group/others)都只有读权限。Composer 执行时若发现无法写入 composer.lock,会立刻报错:
file_put_contents(./composer.lock): failed to open stream: Permission denied
此时你必须主动干预:要么 chmod 644 composer.lock 再跑 composer update,要么确认真不需要更新就跳过。这种“阻断式保护”比事后检查更可靠。
- 别用
555(可执行)——Composer 不需要执行权限,反而可能在某些严格环境报错 - Windows 用户可用
attrib +R composer.lock达到类似效果 - CI 流水线里建议在
install前加这步:chmod 444 composer.lock || true(加|| true防止因文件不存在而中断)
composer install --no-scripts --no-plugins 不解决 lock 被改问题
这两个参数只是跳过脚本和插件执行,跟是否写 composer.lock 完全无关。真正决定 lock 是否被重写的,只有两个条件:
- 你运行的是
composer install还是composer update——前者默认不改 lock,后者一定改 - 当前
composer.json和composer.lock是否一致 ——如果不一致,install会警告但不会自动重写;但如果手动删了 lock 或用了--ignore-platform-reqs等非常规参数,行为可能偏离预期
所以靠参数“预防修改”是错觉,唯一靠谱的是让文件系统拒绝写入。
Git 里怎么避免 lock 文件权限被重置
Git 默认不跟踪文件权限变更,chmod 444 提交后,别人 git clone 或 checkout 时权限大概率变回 644。解决方案有两个:
- 在项目根目录加
.gitattributes,写入:composer.lock permissions=0444(需 Git 2.19+,且仓库启用core.filemode) - 更通用的做法:把
chmod 444 composer.lock写进package.json的postinstall,或 CI 的 setup 步骤里,每次拉代码后自动加固
别指望一次设置永久生效,只读锁本质是运行时防护,得在每次需要保护的上下文里重新施加。
最常被忽略的一点:Docker 构建时,如果 COPY 进去的 composer.lock 是普通权限,RUN 中的 composer install 仍可能覆盖它——得在 COPY 后立刻 RUN chmod 444 composer.lock,顺序不能错。









