Composer私有包失效主因是repositories配置错误:必须为数组而非对象,且需显式添加{"packagist.org": false}禁用默认源;type类型须与URL匹配,锁文件会固化旧源信息。

私有包装不上、公共包也断供,八成是 repositories 数组写成了对象,或漏了 "packagist.org": false —— 这两个错误会让 Composer 静默跳过你配的所有源。
repositories 必须是数组,不是对象
Composer 只识别 JSON 数组形式的 repositories,写成键值对对象(比如 {"my-repo": {}})会导致它只读最后一个元素,前面全被忽略,且不报错。
- ✅ 正确写法:
"repositories": [ {"type": "composer", "url": "https://pkgs.internal"}, {"packagist.org": false} ] - ❌ 错误写法:
"repositories": { "internal": {"type": "composer", "url": "..."} }—— 这个结构在 JSON 规范里合法,但 Composer 不处理 - 顺序即路由优先级:第一个匹配
vendor/name的仓库就终止查找,后面所有源(包括packagist.org)都不再检查
type 选错,URL 再对也拉不到包
type 不是可选项,它决定了 Composer 怎么跟 url 通信。填错类型,请求就发错地方,典型表现是 Could not find package 或卡在 404/401。
-
type: "composer"→url必须返回有效的packages.json(如 Satis、Private Packagist、阿里云镜像),支持全局搜索 -
type: "vcs"→url必须是可git clone的地址(如https://gitlab.com/org/pkg.git),仅当你require了该仓库里明确存在的vendor/name时才触发 -
type: "package"→ 手动写死单个包的版本和 dist 地址,不参与索引,维护成本高,慎用 - 常见错误:把私有 Packagist 的 URL 配成
vcs,结果 Composer 尝试git clone一个 HTML 页面
packagist.org 不是默认开着的,也不是默认关着的
一旦你声明了 repositories 数组,Composer 就自动关闭隐式 packagist.org;但如果你没显式禁用它,它又会作为兜底源排在所有你写的源之后——这会导致私有包被官方旧版覆盖,或认证失败后 fallback 到公开源。
- 要纯私有环境:在
repositories数组末尾加一项{"packagist.org": false}(必须是独立数组项) - 要私有优先 + 公共兜底:把
https://packagist.org显式加回数组末尾,type仍为"composer" - ⚠️ 注意:
"packagist.org": false是composer.json根级字段,不能塞进repositories里的某个对象里 - 别写
{"type": "composer", "url": "https://packagist.org"}来“启用”它——这会冲突报错Cannot use package packagist as it is used as a repository
改了配置,composer install 没反应?锁文件在拦路
composer.lock 锁死了具体版本、dist URL 和源地址三元组。哪怕你把私有源调到第一位、"packagist.org": false 也加了,只要 lock 文件里记的是旧地址,install 就不会重新解析源。
- 安全更新源信息:运行
composer update --lock,它只重写 lock 文件中的源字段,不改动已安装包 - 彻底重来(仅限开发):删掉
composer.lock,再跑composer install - 临时验证是否命中正确源:加
-vvv参数执行composer require vendor/package,看日志里实际请求的 URL 是哪个 - 认证失败(401)永远卡住?确认
auth.json在项目根目录或全局路径,且格式正确;Composer 不读~/.netrc或 git credential
最常被忽略的一点:多个 vcs 源共存时,它们之间完全不互通——每个只服务自己 require 的那个包名,不会合并索引,也不会 fallback。想让私有包一定走内部源,唯一可靠路径是 type: "composer" + 正确的 packages.json + 显式禁用兜底。











