这项决策起草于 2026-08-05,随 2026-08-08 的 SOW v0.2.0 发布。SOW 0.5.0 仍使用这一布局: 每个仓库保存一棵包体树,各发行版视图引用同一棵树。
决策
每个 Repository 的公共目录如下:
上图展示 RPM 视图。APT 索引位于 dists/<dist>/main/binary-<architecture>/,
Release 文件位于 dists/<dist>/。
一个包对象在一个仓库内只有一个包体路径。将同一个包加入多个 Dist 或架构视图,只增加成员关系和元数据引用。
复制、静态托管、鉴权和发布都以完整的 pool/ + dists/ 根目录为单位。
单个架构目录是客户端视图,不是完整的软件仓库。
从 C2 开发线到 v0.2.0
早期 C2 开发线在根包池中保存一份包体,再在每个 RPM 视图内创建包体硬链接。
这让部分 reposync 客户端可以直接使用独立的架构目录,同时节省本地空间。
硬链接在其他环节带来几个问题:
- 对象存储把每条路径存为独立对象,重新产生包副本。
- 归档和跨文件系统复制可能丢失硬链接关系。
- 视图内的包路径看起来像独立文件,实际仍由仓库管理。
- 保留与回收容易依赖链接计数,而不是实际包引用。
- 仓库布局需要迁就特定镜像工具的输出目录要求。
v0.2.0 发布前,最终布局删除了 RPM 视图中的包体别名。归档中的 C2 文档描述的是该开发线。
元数据如何引用包池
APT 的 Filename 是相对仓库根目录的路径,因此可以直接引用包体:
RPM 的 <location href> 使用从视图目录到包池的相对路径:
渲染器和检查器使用同一个路径函数。函数计算相对路径、转义文件名,再检查解析结果是否仍指向仓库内的原包路径。
只要 pool/ 与 dists/ 的相对位置不变,就可以把整个公共目录复制到其他目录、HTTP 站点、桶或前缀,
无需改写元数据。
其他方案
| 方案 | 未采用的原因 |
|---|---|
| 每个 RPM 视图放包体硬链接或副本 | 产生路径别名和重复对象存储键 |
| 包体符号链接 | 对象存储不支持,静态服务与归档的处理也不一致 |
使用 /pool/... 绝对路径 |
客户端可能从站点根目录解析,破坏带前缀的仓库 URL |
绝对 URL 或 xml:base |
将元数据绑定到一个站点,搬迁时需要修改 |
| CDN 改写或重定向对象 | 仓库依赖额外的路由配置 |
| 工作区或桶级共享包池 | 需要跨仓库协调锁、保留与删除 |
为镜像工具导出独立目录
包管理器需要解析元数据引用并下载包体。镜像工具还可能要求所有软件包都位于指定视图目录之下。
需要独立 RPM 架构目录时,运行 sow export rpm-leaf。命令在仓库外生成包文件和指向本地文件的元数据。
默认复制包体;同一文件系统可使用 --hardlink,但导出包必须保持只读,因为写入硬链接也会修改包池文件。
导出目录不属于 Generation,不是发布源,也不参与成员关系或垃圾回收。
客户端与镜像工具测试见兼容性参考。
用 sow check 校验本地路径、摘要和文件引用,用对应客户端测试安装与镜像下载。
结果
- 包身份、成员关系、Generation、保留与本地 GC 都在仓库内管理。
- 发布尝试、检查点、远端清单、宽限期与删除记录按目标前缀管理。
- 每个包文件对应一个静态对象存储键。
- 删除包体前检查引用和文件身份,不依赖硬链接计数。
- 复制完整仓库根目录,或通过导出命令生成独立的 RPM 架构目录。
