必须用satis构建静态镜像,因其生成含packages.json和zip的http可访问目录;path协议仅限本地、无元数据、不支持跨机访问,且composer 2+已禁用file://协议。

直接用 Satis 构建静态镜像,不是 rsync 或 git clone 官方源——后者根本不能被 Composer 识别为合法仓库。
为什么不能用 path 协议或文件共享当镜像
很多人在 composer.json 里写 {"type": "path", "url": "./packages/*"},以为把 ZIP 放进共享目录就能用。实际它只在当前机器本地生效:
• 该配置不启动任何 HTTP 服务,其他机器无法通过 http:// 访问
• file:// 协议在 Docker、CI 构建机、不同用户权限下基本不可用
• 不生成 packages.json 元数据,composer require monolog/monolog 会直接报 Could not find package
• 删掉本地目录,整个依赖树就崩,毫无容错和复用能力
Satis 构建时三个字段缺一不可
必须确保 satis.json 中以下三项全部存在且正确:
-
"require-all": true——否则只同步composer.json里显式require的包,私有仓库里的其他 tag、dev 分支、历史版本全丢 -
"archive"块完整:{"directory": "dist", "format": "zip", "skip-dev": true}——缺这个,CI 构建时只能走git clone,而内网往往禁用 git 协议 -
"repositories"必须是对象数组,每个私有包要单独声明类型:{"type": "vcs", "url": "https://gitlab.internal/internal-sdk"}——写成字符串 URL 或漏掉type,Satis 构建时直接跳过该仓库
全局镜像配置必须严格匹配格式
运行 composer config -g repo.packagist,输出必须是类似:
{"type":"composer","url":"https://packagist.internal/"}
注意几个关键点:
• 键名必须是 repo.packagist(不是 repos.packagist 或 packagist.org)
• type 字段不能丢,否则 Composer 静默 fallback 回官方源
• url 必须带 https:// 和末尾斜杠;http:// 在新版 Composer 默认被拒绝,除非加 --allow-http
• 如果输出为空或报错,说明全局配置根本没写入;常见原因是 PHP 禁用了 proc_open 或 putenv,需检查 php.ini 并重启 PHP 服务
离线机上必须用 HTTP 服务托管,不能用 file://
Composer 2+ 明确禁用 file:// 协议作为仓库源,哪怕路径完全正确,也会报 Invalid repository type: file:。
• 把 Satis 生成的 web/ 目录放到 Nginx/Apache 下,确保能通过 http://192.168.10.100/packages.json 访问到内容
• 关键检查点:用 curl -I http://192.168.10.100/packages.json 看响应头是否含 Content-Type: application/json;Nginx 默认可能返回 application/octet-stream,需手动配置 MIME 类型
• 验证 ZIP 是否可用:检查 web/dist/ 下是否有 ZIP 文件,并确认 packages.json 里对应包的 dist.url 是相对路径(如 dist/monolog-monolog-2.9.1.zip)
最容易被忽略的是 archive 段是否真正启用,以及 packages.json 是否被正确生成并放在根目录——这两项出错,composer install 就会卡在元数据解析阶段,而不是下载失败。











