私有包版本标签规范与语义化版本
解读
在国内 PHP 面试中,面试官问“私有包版本标签规范与语义化版本”通常想确认三件事:
- 你是否真的用 Composer 管理公司内部的私有包,而不是只会写业务代码;
- 你是否理解 SemVer 2.0 的三段式规则,并能在多人协作下避免“幽灵版本”或“破环升级”;
- 你是否能把 Git tag、Composer version、Packagist/Satis 私有仓库、CI 自动化串联成一套可落地的流程。
回答时要把“语义化版本”与“Git 标签命名”挂钩,再落到 Composer 的 minimum-stability、version 字段、alias、branch-alias 等细节,最后给出一条可在阿里/腾讯/字节内部复制的规范示例。
知识点
- 语义化版本号格式:主版本.次版本.修订号(MAJOR.MINOR.PATCH),预发布版加
-alpha.1、-beta.2、-rc.1,构建元数据加+20240618.sha.abc123。 - 私有包常见来源:GitLab 私有组、Gitea、Gitee 企业版、阿里云 Codeup、腾讯工蜂、自建 Git+Satis/Repman。
- Composer 识别版本优先级:
- 正式 tag > branch-alias > 分支名-dev >
@stable>@dev。 - 若 tag 为
v1.2.3,Composer 内部解析成1.2.3.0;若 tag 为release/1.2.3,需配合branch-alias才能被识别。
- 正式 tag > branch-alias > 分支名-dev >
- 国内镜像陷阱:阿里云 Composer 镜像默认缓存 12 h,tag 推送后需主动
composer clear-cache或composer update --lock才能拉取最新。 - 破环升级红线:
- 主版本号升级必须删方法、改命名空间、调接口路径;
- 次版本号只能加向后兼容功能;
- 修订号只能修 bug 且不能改 public 方法签名。
- 自动化校验脚本:
- 在 GitLab CI 里用
composer validate --strict检查 composer.json; - 用
composer-require-checker扫描是否误删依赖; - 用
roave/backward-compatibility-check在 MR 阶段强制检测 BC Break。
- 在 GitLab CI 里用
- 国内合规要求:若私有包含支付、身份证字段,需在 tag 日志里记录《数据安全法》要求的“变更原因+影响范围+责任人”。
答案
我们团队把私有包版本规范拆成“三段式 + 两阶段 + 一键发布”:
- 三段式版本号
正式环境只允许X.Y.Z格式,禁止v前缀;预发布环境用X.Y.Z-rc.n,灰度环境用X.Y.Z-beta.n,daily 构建用X.Y.Z-alpha.n+build.{date}。 - 两阶段标签
开发阶段:开发者在 feature 分支打临时标签dev-feature/foo或直接用branch-alias,例如{"dev-master": "2.5.x-dev"},禁止推到生产 Satis。
发布阶段:合并到 master 后由 CI 自动读取CHANGELOG.md的## [Unreleased]小节,提取升级类型(major/minor/patch),生成正式 tag 并推送到内部 GitLab。 - 一键发布脚本(GitLab CI 片段)
release: stage: release only: - master script: - export VERSION=$(conventional-recommended-bump) - git tag $VERSION -a -m "chore(release): $VERSION see CHANGELOG.md" - git push origin $VERSION - satis build satis.json public/ - Composer 消费侧约束
业务项目composer.json要求:
这样即使私有仓库出现"minimum-stability": "stable", "prefer-stable": true, "require": { "company/pay-core": "~2.5.0" }2.6.0,也只会在 QA 环境显式升级,生产不会误升。 - 破环升级审批
主版本号升级必须发“内部 RFC”,经架构组、测试组、安全组三方评审,并在 tag 附注里附带《兼容性影响清单》链接,否则 CI 拒绝推送 tag。
落地一年后,我们 200+ 私有包零次意外破环,回滚次数从月均 3 次降到 0 次。
拓展思考
- 如果公司收购另一家 PHP 团队,对方使用
v1.2.3带v前缀,而你们不带,如何让 Composer 统一解析?
答:在 Satis 的config段加"prepend-v": false,强制去掉v;同时写一条 GitLab CI 规则,检测到新 tag 带v时自动打同名无vtag 并删除旧 tag,保证历史兼容。 - 国内银行项目要求“可审计”,如何证明某个正式 tag 的代码与上线包一致?
答:把 Satis 生成的distzip 包做 SHA256 并写入composer.lock,同时在 GitLab Release 里上传同名tar.gz与 checksum,上线脚本用composer install --no-dev --prefer-dist后二次校验哈希, mismatch 直接终止部署。 - 当私有包依赖另一个私有包,且两者版本节奏不同,怎样避免“钻石依赖”冲突?
答:在根项目用replace或conflict声明冲突范围,同时把公共私有包拆成“接口包”和“实现包”:接口包保持 1.x 长期稳定,实现包按季度发 2.x、3.x,业务代码只依赖接口包,实现包在部署阶段由容器镜像一次性锁定,从而把 Composer 冲突转化为运维层的镜像冲突,降低开发侧复杂度。