Composer 的 version 字段在 Monorepo 中无效,真实版本仅来自 Git tag(如 v1.2.3)或分支名(如 dev-main),且需配合 path repository 和 --no-dev 安装;硬写 version 无用,应通过 replace/provide 统一管理依赖,并用 monorepo-builder 同步版本文件。

Composer 的 version 字段在 Monorepo 中不生效
Monorepo 里每个包都写 "version": "1.2.3" 是徒劳的——Composer 完全忽略它,除非你手动发布到 Packagist 或私有仓库。本地路径依赖("type": "path")下,Composer 只认 composer.json 所在目录的 Git tag 或分支名,不是文件里的 version。
常见错误现象:composer update 后子包版本没变、composer show 显示 dev-main 而非你写的 1.2.3、CI 构建失败提示“package not found”。
- 真实版本来源只有两个:Git tag(如
v1.2.3)或分支名(如dev-main),且必须由composer install时从源码仓库解析 - 如果你用
"repositories"声明了本地path类型,记得加"options": {"symlink": false},否则 Windows 下可能因符号链接失效导致版本识别错乱 - 别在
composer.json里硬写version并指望它被消费——它只在 packagist.org 上发布后才起作用
用 composer.json 的 replace 和 provide 统一约束依赖版本
Monorepo 内部多个包互相依赖时,靠改每个 require 的版本号太脆弱。真正可控的方式是让 root composer.json 用 replace 占位,再用 provide 声明能力,把版本控制权收归一处。
使用场景:比如 packages/http-client 和 packages/logger 都需要绑定同一套 PSR 接口版本,但又不想每次升级都手动改十几个 composer.json。
- 在根目录
composer.json中添加:"replace": { "myorg/http-client": "self.version", "myorg/logger": "self.version" }—— 这样所有子包都能通过myorg/*被统一识别为当前 monorepo 版本 - 在子包中用
"provide"声明兼容性:"provide": {"psr/log-implementation": "1.0"},避免重复安装冲突的实现 - 注意:
replace不会自动安装包,只是告诉 Composer “这些包已存在”,所以仍需用pathrepository 正确加载源码
Git tag + composer install --no-dev 是发布唯一可信路径
Monorepo 发布不是改个数字再 composer update 就完事。真实交付版本必须对应一个 Git tag,且构建环境必须干净(无 dev 分支残留)。
容易踩的坑:git checkout v1.2.3 && composer install 后发现 vendor/ 里还是 dev-main;或者 CI 中用了 --prefer-source 导致检出的是带 .git 的完整克隆,tag 信息反而被忽略。
- 确保 tag 名严格匹配
vX.Y.Z格式(前面带v),Composer 默认只识别这种格式 - CI 构建时用:
git fetch --tags && git checkout v1.2.3 && composer install --no-dev --optimize-autoloader - 验证是否生效:运行
composer show myorg/http-client,输出应为versions : * v1.2.3,而不是dev-main或空行
monorepo-builder 不是银弹,但能绕过 Composer 原生限制
Composer 本身没有 Monorepo 模式,官方也不打算加。想自动同步版本号、生成 changelog、批量打 tag?得靠工具补足,monorepo-builder 是目前最轻量靠谱的选择。
它不替换 Composer,而是提前把版本号注入每个子包的 composer.json,再调用原生命令。关键在于:它操作的是文件内容,不是依赖解析逻辑。
- 配置
monorepo-builder.json时,"version-file"指向根目录的VERSION文件,内容就一行:1.2.3 - 执行
php monorepo-builder.php release后,所有子包的composer.json会被重写version字段,并自动git commit -m "chore: release v1.2.3" - 但它不会解决 runtime 的版本识别问题——最终仍要靠 Git tag +
pathrepo 配合,否则composer show还是看不到你写的那个数字
版本号不是写在文件里就等于生效,它必须穿过 Git、Composer、CI 三道关卡才能被下游真正感知。漏掉任意一环,看到的都是 dev-main。










