私有包版本标签规范与语义化版本

解读

在国内 PHP 面试中,面试官问“私有包版本标签规范与语义化版本”通常想确认三件事:

  1. 你是否真的用 Composer 管理公司内部的私有包,而不是只会写业务代码;
  2. 你是否理解 SemVer 2.0 的三段式规则,并能在多人协作下避免“幽灵版本”或“破环升级”;
  3. 你是否能把 Git tag、Composer version、Packagist/Satis 私有仓库、CI 自动化串联成一套可落地的流程。
    回答时要把“语义化版本”与“Git 标签命名”挂钩,再落到 Composer 的 minimum-stability、version 字段、alias、branch-alias 等细节,最后给出一条可在阿里/腾讯/字节内部复制的规范示例。

知识点

  1. 语义化版本号格式:主版本.次版本.修订号(MAJOR.MINOR.PATCH),预发布版加 -alpha.1-beta.2-rc.1,构建元数据加 +20240618.sha.abc123
  2. 私有包常见来源:GitLab 私有组、Gitea、Gitee 企业版、阿里云 Codeup、腾讯工蜂、自建 Git+Satis/Repman。
  3. Composer 识别版本优先级:
    • 正式 tag > branch-alias > 分支名-dev > @stable > @dev
    • 若 tag 为 v1.2.3,Composer 内部解析成 1.2.3.0;若 tag 为 release/1.2.3,需配合 branch-alias 才能被识别。
  4. 国内镜像陷阱:阿里云 Composer 镜像默认缓存 12 h,tag 推送后需主动 composer clear-cachecomposer update --lock 才能拉取最新。
  5. 破环升级红线:
    • 主版本号升级必须删方法、改命名空间、调接口路径;
    • 次版本号只能加向后兼容功能;
    • 修订号只能修 bug 且不能改 public 方法签名。
  6. 自动化校验脚本:
    • 在 GitLab CI 里用 composer validate --strict 检查 composer.json;
    • composer-require-checker 扫描是否误删依赖;
    • roave/backward-compatibility-check 在 MR 阶段强制检测 BC Break。
  7. 国内合规要求:若私有包含支付、身份证字段,需在 tag 日志里记录《数据安全法》要求的“变更原因+影响范围+责任人”。

答案

我们团队把私有包版本规范拆成“三段式 + 两阶段 + 一键发布”:

  1. 三段式版本号
    正式环境只允许 X.Y.Z 格式,禁止 v 前缀;预发布环境用 X.Y.Z-rc.n,灰度环境用 X.Y.Z-beta.n,daily 构建用 X.Y.Z-alpha.n+build.{date}
  2. 两阶段标签
    开发阶段:开发者在 feature 分支打临时标签 dev-feature/foo 或直接用 branch-alias,例如 {"dev-master": "2.5.x-dev"},禁止推到生产 Satis。
    发布阶段:合并到 master 后由 CI 自动读取 CHANGELOG.md## [Unreleased] 小节,提取升级类型(major/minor/patch),生成正式 tag 并推送到内部 GitLab。
  3. 一键发布脚本(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/
    
  4. Composer 消费侧约束
    业务项目 composer.json 要求:
    "minimum-stability": "stable",
    "prefer-stable": true,
    "require": {
        "company/pay-core": "~2.5.0"
    }
    
    这样即使私有仓库出现 2.6.0,也只会在 QA 环境显式升级,生产不会误升。
  5. 破环升级审批
    主版本号升级必须发“内部 RFC”,经架构组、测试组、安全组三方评审,并在 tag 附注里附带《兼容性影响清单》链接,否则 CI 拒绝推送 tag。

落地一年后,我们 200+ 私有包零次意外破环,回滚次数从月均 3 次降到 0 次。

拓展思考

  1. 如果公司收购另一家 PHP 团队,对方使用 v1.2.3v 前缀,而你们不带,如何让 Composer 统一解析?
    答:在 Satis 的 config 段加 "prepend-v": false,强制去掉 v;同时写一条 GitLab CI 规则,检测到新 tag 带 v 时自动打同名无 v tag 并删除旧 tag,保证历史兼容。
  2. 国内银行项目要求“可审计”,如何证明某个正式 tag 的代码与上线包一致?
    答:把 Satis 生成的 dist zip 包做 SHA256 并写入 composer.lock,同时在 GitLab Release 里上传同名 tar.gz 与 checksum,上线脚本用 composer install --no-dev --prefer-dist 后二次校验哈希, mismatch 直接终止部署。
  3. 当私有包依赖另一个私有包,且两者版本节奏不同,怎样避免“钻石依赖”冲突?
    答:在根项目用 replaceconflict 声明冲突范围,同时把公共私有包拆成“接口包”和“实现包”:接口包保持 1.x 长期稳定,实现包按季度发 2.x、3.x,业务代码只依赖接口包,实现包在部署阶段由容器镜像一次性锁定,从而把 Composer 冲突转化为运维层的镜像冲突,降低开发侧复杂度。