canonical是composer 2.x默认启用的规范仓库机制,即按repositories声明顺序查找包,一旦某仓库返回匹配结果(含“未找到”响应),立即终止后续搜索;若packagist.org排在私有仓库前且返回“无此包”,私有仓库便被跳过,导致包“消失”。

什么是canonical,为什么它会让私有包“消失”
Composer 2.x 默认启用 canonical(规范)仓库机制:所有 repositories 按声明顺序逐个查找包,一旦某个仓库返回了匹配的包(哪怕只是元数据),后续仓库就完全不查了——包括你自己的私有仓库。这不是 bug,是设计行为。
典型现象是:composer require acme/utils 报 Could not find package,但你确认私有库 URL、auth.json、name 字段全对;根源往往是 Packagist.org 在列表里排在前面,它先响应了“没这个包”,Composer 就不再往下走,根本没机会访问你的 GitLab 或 Satis。
关键点在于:canonical 不是开关,而是默认行为;要绕过它,必须显式声明 "canonical": false。
如何禁用 canonical 行为(vcs 和 composer 类型都适用)
在项目级 composer.json 的 repositories 数组中,为每个需要“非规范”行为的仓库添加 "canonical": false 字段:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
{
"repositories": [
{
"type": "vcs",
"url": "https://gitlab.example.com/acme/utils.git",
"canonical": false
},
{
"type": "composer",
"url": "https://satis.internal/packages/",
"canonical": false
}
]
}
注意以下几点:
-
"canonical": false必须写在每个仓库对象内部,不能提一层放到repositories外 - 如果同时用了 Packagist 镜像和私有源,建议把私有源放前面 +
"canonical": false,避免镜像提前截断 - Artifactory 或 Private Packagist 类型仓库也需加该字段,否则它们的元数据可能被 Packagist 的空响应覆盖
- 运行
composer show --platform不会显示 canonical 状态,验证是否生效只能靠composer install -v日志中是否出现私有源的 GET 请求
packagist.org: false 和 canonical: false 的区别与共存
两者解决不同层级的问题,经常要一起用:
-
"packagist.org": false是全局关掉默认源,让 Composer 完全忽略 Packagist.org(包括它的镜像),适用于纯内网环境 -
"canonical": false是保留多个源并行搜索的能力,适用于混合场景:比如先查 Satis,查不到再 fallback 到 GitLab,最后才试 Packagist - 二者不冲突,可以共存;但如果你写了
"packagist.org": false,再写"canonical": false就多余了——因为只剩私有源,天然无优先级竞争 - 误写成
"packagist": false或"packagist.org"缺少引号,Composer 会静默忽略,不报错也不生效
canonical false 后仍找不到包?检查这三处硬性约束
禁用了 canonical 只是打开了多源搜索的门,不代表包一定能被识别。以下任一条件不满足,composer install 依然失败:
- 私有仓库 URL 必须可直接
git clone:HTTPS 地址末尾带.git,SSH 地址格式为git@gitlab.example.com:group/repo.git;写成网页地址会被跳过 -
auth.json必须放在正确路径且权限为600:Linux/macOS 是~/.composer/auth.json,Windows 是%APPDATA%\Composer\auth.json;项目根目录下的auth.json默认不读 - 私有包自身
composer.json中的name字段,必须与主项目require中的字符串**逐字符一致**,包括大小写、斜杠方向、vendor 名长度——acme/utils≠Acme/utils≠acme-utils
最容易被忽略的是:即使所有配置都对,如果私有 Git 仓库的默认分支不是 main 或 master,而你 require 写的是 "dev-main",但实际分支叫 dev-stable,Composer 就不会拉取——它不猜分支名,只认你写的字面量。










