答案是所有权污染而非并发问题,需用chown -R $USER:$USER修复vendor/等目录归属,禁用sudo composer install以防root属主导致执行拒绝。

vendor/bin 脚本执行权限丢失不是并发问题,而是所有权污染
报 Permission denied 时,95% 情况下根本没发生并发竞争——是 sudo composer install 导致 vendor/ 目录属主变成 root,后续普通用户 shell 尝试执行 vendor/bin/phpunit 时被内核拒绝。Linux 不检查文件是否可执行(x 位),而是先验“你有没有权限进入这个目录 + 读取这个文件 + 执行这个文件”,其中任意一项失败都报错。而属主为 root 的 vendor/ 目录,普通用户连 cd vendor/bin 都可能被拒(取决于目录权限),更别说执行里面脚本。
常见误操作包括:
- 在 CI 中用
sudo -u root composer install,但没清理vendor/属主 - 本地开发误用
sudo composer require,污染整个vendor/ - Docker 构建中 base image 以
root用户运行composer install,未切换到非 root 用户再 chown
并发 install 不会破坏 vendor/bin 权限,但会破坏 lock 文件一致性
composer install 本身不修改 vendor/bin 下脚本内容或权限位;它只是按 composer.lock 复制/解压已打包好的脚本文件。这些脚本的权限信息来自 ZIP 包元数据或 Git 索引,不是 Composer 运行时生成的。所以多个 install 进程同时跑,不会导致某个 phpunit 文件突然丢掉 x 位。
真正并发风险在 composer.lock 文件上:
- 两个进程同时读取旧版
composer.lock,各自解析依赖后写回新版本 → 后写入者覆盖前写入者 - CI job A 执行
composer install,job B 同时执行composer update→lock被重写,A 的安装中途可能加载错误版本 - Git 仓库中
composer.lock哈希不一致,触发install报错 “lock file is not up to date”
解决方案不是加 chmod +x,而是加锁:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- Linux/macOS:用
flock ./composer.lock -c 'composer install --no-interaction' - GitHub Actions:用
concurrency: group: ${{ github.head_ref }}锁定分支 - 禁止任何自动化流程执行
composer update,只允许人工 PR 合并 lock
bin 命令冲突与并发无关,但高密度部署会放大影响
多个包声明同名 bin(如都提供 phpunit)时,Composer 总是让后安装的覆盖先安装的,且无提示。这和并发无关,但当你在 CI 中高频部署、反复 rm -rf vendor && composer install,就容易因依赖解析顺序微小变化,导致某次构建中 vendor/bin/phpunit 指向了低版本或不兼容版本。
稳定做法是绕过自动映射:
- 在
composer.json中设"bin-compat": "full",确保 Windows/WSL 下也能识别执行位 - 不用
vendor/bin/phpunit,改用php vendor/bin/phpunit—— 这样跳过 shell 的x权限检查,只依赖 PHP 的文件读取权限 - 对关键工具显式指定路径:
./vendor/bin/phpunit-9(需提前在bin字段声明别名)
Docker 和 CI 环境里最常漏掉的三件事
很多团队卡在“本地好好的,CI 报 Permission denied”,其实只差三步:
- 构建镜像时,
composer install必须在非 root 用户上下文中运行,或紧接着执行chown -R www-data:www-data /app/vendor - CI runner 的缓存目录(如
~/.composer/cache)必须属主为当前 runner 用户,否则Writing cache file失败会静默降级,导致后续 install 用错包 - 不要在
ENTRYPOINT或docker-compose.yml中硬编码vendor/bin/sail,应改用php vendor/bin/sail或挂载~/bin到 PATH
真正麻烦的从来不是 chmod 命令怎么敲,而是谁在哪个 UID 下写了哪个文件、又由谁去读它——权限问题本质是身份归属问题,不是数字位设置问题。

















