Sunsetting a Package ManagerSunsetting a Package Manager
Sunsetting a Package Manager
Andrew Nesbitt
2026年12月2日,CocoaPods trunk 将转为只读状态。Orta Therox 在2024年制定了这个计划:服务器将停止接受新的 pod 和新版本,而已发布的超过 10 万个库将继续通过 GitHub 上的 Specs 仓库和 jsDelivr 的 CDN 进行解析,因此每个现有的 Podfile 的解析结果将与前一天完全一样。维护者已经耗尽了人手,Apple 的 Swift Package Manager 已成为首选工具,加上2024年的一系列严重漏洞,使得运行 trunk 服务器成了一种负担。
仍有大量用户存在:React Native 仍然通过 CocoaPods 解析其 iOS 依赖,迁移到 SPM 还是一个活跃的 RFC 而非一条已完成的路径,并且大量长尾应用将在冻结后很长一段时间内继续运行 pod install。一个停止接受上传的注册中心仍然会为所有已经依赖它的用户提供安装服务,CocoaPods 维护者并不是第一个应对这种情况的团队。在他们之前,已有四个注册中心被冻结或关闭:
只读的不同程度
只读涵盖了多种状态,从拒绝新上传的服务器,到比运营它的组织寿命更长的数据集,而 CocoaPods 有意选择了最远的那一端。Specs 索引已经存在于 GitHub 仓库中,并且包由 jsDelivr 提供服务,因此这次冻结退役了团队运营的 trunk 服务器,将索引和包留在了不由 CocoaPods 运营的基础设施上,这是在打赌 GitHub 和 jsDelivr 的存活时间会超过人们对它的需求。
Atom 的 apm 走了另一条路:当 GitHub 在2022年12月将该编辑器归档时,包后端也随之一同退役,apm install 随即停止工作。要在冻结后保持安装可用,需要进行刻意的安排。
JFrog 为 JCenter(它从 Bintray 继承的 Java 和 Android 注册中心)做出了相反的安排。当 Bintray 在2021年5月1日被弃用时,JFrog 将 JCenter 保留为“一个只读仓库,以减轻社区的负担”,并在自己的基础设施上以这种方式运行了三年。Apache 的 Attic 也是同样的最终状态,只不过附带了一套流程:一个社区已经解散的项目会被移到那里,其代码保持可读状态,但不再发布任何新内容。在这两种情况下,运营注册中心的组织都持有数据本身。
Bower 处于另一个极端:它的注册中心只存储包名和 git URL,版本是直接从仓库的 git 标签实时读取的,而不是集中保存。当它停止接受新注册时,现有的包继续不受影响地进行解析。Bower 包直接从其所在的仓库(代码和标签所在的地方)安装,因此这次冻结只去掉了新名称的注册,仅此而已,只要每个仓库保留其 bower.json 即可。
自2000年以来,Perl 就保留了 BackPAN 来专门解决究竟由谁来保存文件的这个问题,它是一个复制上传并忽略删除的镜像,因此即使作者删除了某个版本,他们曾经推送的每个版本仍然保持可获取状态。一个被冻结的注册中心也需要相同的答案:如果 jsDelivr 有一天放弃了 CocoaPods 镜像,谁持有这 10 万个 podspec 的完整副本将成为一个紧迫的问题,最好在数据仍然有效可用时给出答案。
重定向还是重写
一个持续提供服务的注册中心不会设定截止日期,因此用户会一直推迟迁移。JFrog 通过“降级”施加了一些压力:在计划的时间窗口内,请求会被重定向到 Maven Central 而不是直接提供服务。该计划从 2024 年 7 月 30 日的两个为期一小时的时间窗口开始,接着是两个四小时的窗口,然后是一整天,直到 8 月 15 日进行永久重定向。这种逐步升级的策略让每一个尚未完成迁移的构建都在永久重定向之前,经历了一个可恢复的失败,从而促使他们做出反应。CocoaPods 已经安排了一个更为温和的版本:在正式切换前一个月,即 2026 年 11 月 1 日至 7 日,进行一次只读模式的试运行。
只有当有等效的目的地来接收流量时,重定向才能生效。JFrog 能够将 JCenter 指向 Maven Central,是因为这两个注册中心在相同的坐标下提供相同的构建产物,因此这种重定向几乎可以直接无缝替换。Bower 没有这样的等效替代:它告诉用户迁移到 npm 和 Webpack,而这是一个具有不同清单文件(manifest)的不同工具,因此这种迁移无异于重写。CocoaPods 的情况更接近 Bower,因为 SPM 无法直接无缝替换 Podfile,放弃 CocoaPods 意味着必须手动重写每个清单文件。
兜底方案(escape hatch)内置于 CocoaPods 的最初设计中:podspec 的源可以指定一个 git tag,而 Podfile 可以直接将依赖项指向某个代码仓库,从而完全绕过 trunk。Therox 的公告指出,此次冻结“不应影响那些使用自己的 specs 仓库的 CocoaPods 用户,或者将所有依赖项都进行本地化引入(vendored)的用户”。在中央索引停止更新很久之后,私有 specs 仓库和基于 git 的 pod 依然能让客户端发挥作用。
基于 git 的 pod 继承了代码托管平台的可变性:已发布的注册中心坐标无法更改,但 git tag 可以被移动;并且如果在同一路径下删除并重新创建仓库,它将沿用原有的 URL,这与转移 GitHub 仓库后仍能继续解析的重定向机制如出一辙。将一个冻结的、不可变的索引替换为一个动态的、可变的代码托管平台,这是一种实质性的权衡,且很容易在不知不觉中发生。
冻结后的补丁修复
此次冻结移除了唯一可以修复 pod 的地方,尽管促成本次冻结的 2024 年漏洞存在于 CocoaPods 自身的基础设施中,而不是在任何已发布的 pod 中:包括 CVSS 9.3 的孤儿 pod 接管漏洞、9.6 的会话劫持漏洞,以及 trunk 服务器本身 10.0 的远程代码执行(RCE)漏洞,所有这些都是通过关闭服务器来解决的。
成本最终落在了 pod 上:在 12 月 2 日之后,如果某个热门库新发现了严重的 CVE,它将没有官方版本来承载修复程序,每个使用者都只能手动去锁定一个 git fork。SDWebImage 就是会被这种机制困住的那种 pod:它处于活跃维护状态,从 trunk 解析并集成到超过 25,000 个仓库中,而其图像解码器往往是内存安全 CVE 的常见来源。一旦 trunk 变为只读,其维护者发布的修复程序将无法进入那些仓库已经指向的坐标。
该团队此前已经将写入限制用作安全工具,在 2025 年 5 月封锁了使用 prepare_command 的新 pod,因为该字段在安装时会运行任意的 shell 命令。而全面冻结只是将这一手段发挥到了极致:它关闭了 trunk 的攻击面,但同时也剥夺了发布任何修复程序的能力。
Linux 发行版几十年来一直在应对这种情况,并为此建立了一个狭义的例外机制:Debian 稳定版除了安全性之外,对所有内容都进行了冻结;在这个静态树中,一个小型团队负责将安全修复向后移植(backport),而 LTS(长期支持)工作则将这个维护窗口期延长,甚至超过了该项目其他部分向前推进的时间点。
一种与日落注册表相适配的模式是仅安全发布通道:主干分支不对功能发布开放,但一组指定的管理员保留发布补丁版本的能力,以便在一个被广泛依赖的组件中发现漏洞时,修复有处可落。这种模式将发布面收得很窄——最小规模的团队加上一条审计轨迹——并接受其替代方案带来的后果:一个已知可被利用的包被冻结为最新版本。
这些都不是没有代价的:需要有人值守该通道,决定哪些CVE够格纳入发布,并掌握一套系统的发布密钥——而这套系统之所以部分关停,恰恰是因为持有这些密钥已变得危险。2024年的漏洞存在于主干服务器的Web技术栈中,而非发布流程本身,因此一条狭窄的发布路径不必重新拉起那台危险的服务器。维护者完全可能合理地决定,让长尾用户自行分叉并锁定版本,而维持一台在线服务器的代价超过了它所能发布的补丁的价值。冻结日期会堵死这一选项,因此仅安全通道值得在切换之前而非在冻结后首个CVE出现时再做评估。
需要完整排版与评论请前往来源站点阅读。