多包自动发布依赖 Git tag + Packagist webhook + CI 协同,而非 Composer 自身;高频失败原因为本地 composer.json 写死 version 或 webhook 配置错误。

多包自动发布不是靠 Composer 自身功能实现的,而是靠 Git tag + Packagist webhook + CI 协同完成;本地 composer.json 里写死 version 或漏配 webhook 事件,是导致“打了 tag 却没更新”的两大高频原因。
Git tag 必须语义化且推送到远程
Packagist 不解析分支或 commit,只认 git tag。你本地打完 git tag v1.2.0,必须执行 git push origin v1.2.0(不是 git push --tags,后者可能推送非目标 tag)。
- Tag 名必须符合语义化版本:
v1.2.0、v2.0.0-beta.1可识别;release-1.2、20260407会被忽略 -
composer.json中的version字段必须删除或注释掉——它会覆盖 tag 解析结果,导致 Packagist 抓到1.0.0却显示为dev-main - Tag 对应的 commit 必须包含合法的
composer.json(含name、type、autoload),否则 Packagist 同步失败但不报错
Packagist webhook 配置必须精确匹配
GitHub/GitLab 的 webhook endpoint 和触发条件稍有偏差,就会静默失效。
- GitHub:Settings → Webhooks → 添加
https://packagist.org/api/github,勾选 Just the push event,并在下方展开勾选 Include all branches and tags(旧版 UI 叫 “Tag push events”) - GitLab:Settings → Webhooks → 填
https://packagist.org/api/gitlab,同样必须勾选 Tag push events - 关键细节:
repository.url字段在 webhook payload 中,必须和 Packagist 上录入的包 source URL 完全一致(比如都用https://github.com/yourname/pkg,不能一个用git@github.com:yourname/pkg.git)
Monorepo 多包需独立打 tag 并隔离仓库
一个仓库多个包(如 packages/a、packages/b),不能共用同一个 tag;Packagist 按包名识别,不是按路径。
- 每个子包要单独配置 Packagist 项目,source URL 填完整路径,例如:
https://github.com/yourname/monorepo/tree/main/packages/a(注意/tree/main/packages/a后缀) - 打 tag 时用命名空间前缀区分,如
a-v1.0.0、b-v2.1.0,再在各自 Packagist 项目中设置tag pattern(如a-v*) - CI 脚本里不要用
composer update vendor/name触发同步——这只能刷新本地 vendor,对 Packagist 无影响;必须走 git push tag
验证失败时优先查 webhook 日志和手动触发
Packagist 不提供实时错误反馈,出问题时得靠外部线索定位。
- GitHub/GitLab webhook 页面里点 “Recent Deliveries”,看 status 是否 200;若为 404 或 400,大概率是 source URL 不匹配或包未被 Packagist 录入
- 用 curl 手动触发:
curl -X POST "https://packagist.org/api/update-package?username=xxx&apiToken=yyy" -d '{"repository":{"url":"https://github.com/yourname/pkg"}}',返回 404 表示包名错误或 token 权限不足 - 别依赖 “Update” 按钮:Packagist 后台的 “Update” 只抓最新 commit,不重新解析 tag;只有新 tag 推送才能触发版本发布
真正卡住的地方往往不在脚本逻辑,而在 Git tag 的命名习惯、webhook 的 URL 格式、以及 Packagist 后台录入时少填了一个斜杠——这些细节不显眼,但改一次就能让整个链路通起来。











